C ¸agdaş

Bozman Ocamlpro

Michael Laporte
email: ocamlpromichael.laporte@ocamlpro.com

Fabrice Le Fessant
email: fabrice.lefessant@inria.fr

ocp-lint A Plugin-based Style-Checker with Semantic Patches

In this talk, we will present ocp-lint, a new style-checker for OCaml projects. ocp-lint can typically be used to check pull-requests in a Github-style workflow. ocp-lint is highly extensible, with a simple API to define new plugins that can be linked dynamically. ocplint is easily configurable, with plugin-and analysis-specific options and arguments. ocplint can also use semantic patches, a patchstyle format to describe code patterns to detect. Warnings found by ocp-lint are stored in a database, to avoid useless recomputations and ease the development of external tools and GUIs to exploit its results.

$ ocp -lint --path tools / ocp -lint --disable -plugin -typedtree Summary : * 11 files were linted * 40 warnings were emitted : * 2 " inter face_m issing " number 1 * 2 " code_length " number 1 * 4 " ocp_indent " number 1 File " lint_input . ml " , line 1:

" ocp_indent " number 1: File lint_input . ml ' is not indented correctly . File " lint_actions . ml " , line 1:

" code_length " number 1: This line is too long ( '82 ') : it should be at most of size '80 '. File " main . ml " , line 1:

" inter face_m issing " number 1: Missing interface for main . ml '.

Introduction

Even in languages with strong static typing, style-checkers can be very useful: some coding styles are known to ease the hidden presence of bugs; other coding styles can be inefficient or raise memory issues. Also, having different coding-styles in a project can make codereview more difficult, disturbing the focus of reviewers towards minor style issues instead of looking for algorithmic issues.

A style-checker can solve many of these problems by providing an automatic way to check for coding styles that should be avoided in a project. For that, the style-checker should be fast, to provide feedback almost immediately, configurable, to allow project leaders to express their preferences, and extensible, to allow project developers to design new analysis specific to their needs.

In this talk, we will present ocp-lint, a stylechecker for OCaml projects, that aims at satisfying all these needs. An overview of ocplint is given in section 2. Of course, ocp-lint builds on our experience learnt from using other style-checkers for OCaml, as shown in section 3. It is easy to use and configure, as depicted in appendix A, and can be extended using both semantic patches, in appendix B, and dynamic plugins using a simple API, described in appendix C.

Overview

The main design idea behind ocp-lint is to provide a framework for checking OCaml projects for coding errors, instead of just another monolithic tool. For that, we have tried to make it easy to extend ocp-lint, either using semantic patches (patterns of code described in a patch-like syntax on OCaml code) or using dynamic plugins (user code linked at runtime). We also tried to make it as configurable as possible: each plugin and each analysis can be enabled or disabled by a simple project configuration file, and analysis settings can be modified in the same configuration file. We wrote a set of plugins and analyses, both to be able to use the tool for our own purposes, and to provide examples for developers of how to extend the tool.

Configuration files in ocp-lint are managed by the ocplib-config [START_REF]ocplib-config[END_REF] library. This library provides simple functions to define options, that can be manipulated as simply as references in the program, while being loaded and saved automatically in configu-ration files. The library also automatically generates command-line arguments to modify the options. ocp-lint can use both userand directory-specific configuration files.

We already implemented a small set of plugins working on different kinds of inputs: text plugin (length of lines, extra spaces, non-ANSII characters, etc.), indent plugin (correct use of ocp-indent), tokens plugin (non-ASCII characters in comments, in idents, etc.), parsetree plugin (constructor with tuple arguments, local aliases, identifiers lengths, semantic patches, etc.), sempatch plugin (detection of patterns provided by semantic patches), typedtree plugin (non-qualified external idents), parsing plugin (extra-parentheses, use of parentheses instead of begin..end, etc.).

Warnings emitted by these analyses are stored in a project-database. The database is then used when the tool is restarted, to avoid checking files again if they have not been modified, and to allow other tools to display the results (ocp-index or Merlin for example, or on a web interface).

3 Related Work ocp-lint is not the only tool that can be used to improve the quality of the code of an OCaml project. In this section, we compare ocp-lint with three other tools that can be used for this purpose.

Mascot [START_REF] Clerc | Mascot[END_REF] was probably the most exhaustive style-checker for OCaml. It provided many checks in various categories: code, documentation, interface, metrics, and typography. However, it is not maintained anymore, and hard to extend, especially as analyses are heavily based on using Camlp4 syntax trees.

ocamllint [3] is a style-checker that runs as ppx [START_REF] Minsky | extension points, or how ocaml is becoming more like lisp[END_REF] while compiling the project. Thus, it requires minimum effort to be used on an OCaml project. However, the number of analyses is currently very limited, and they can only be applied on the AST, whereas ocp-lint can work also on text files, and on typedtrees.

Dead code analyzer [START_REF] Lexifi | Ocaml dead code analyzer[END_REF] tries to detect useless patterns in an OCaml project. For example, it detects never used values, types fields and constructors (that can thus be removed as dead code), and optional labels either always or never used. The tool assumes that interface files (.mli) are compiled with the -keep-locs and source files (.ml) with -bin-annot. The analysis can be quite expensive, but the tool is a good complement to ocp-lint, and could be added as a plugin to benefit from its database and project management.

A Usage ocp-lint can use a database to make stylechecking a project. To create an initial database, the user should call ocp-lint -init, otherwise, no database will be used.

Then, here is a typical example of running ocp-lint:

The --path argument tells ocp-lint the directory to scan for files to check. ocp-lint will check all the files with extensions .ml, .mli, .cmt and .cmti. We are currently parallelizing the process of checking each file in a different process: each internal process will store its results in the (temporary or persistent) database and the calling process will display the results at the end.

Each plugin can define its own arguments that are added to ocp-lint command-line, to:

• enable/disable each plugin (e.g. --disable-PLUGIN);

• enable/disable each plugin analysis (e.g.

--disable-PLUGIN.LINTER);

• enable/disable each linter set of warnings (e.g. --disable-PLUGIN.LINTER.warnings -A+3..5+8);

• set analysis settings a set of parameters (e.g. --PLUGIN.LINTER.OPTION VALUE);

The --load-plugins FILE.cmxs argument can be used to load a plugin dynamically (ocp-lint comes with a few plugins already statically linked), and the -list argument can be used to list plugins and their analyses.

Options changed on the command-line can be saved in the project configuration file using the --save-config argument.

Several output formats can be used to print warnings: plain-text, JSON, HTML, etc. This is easily extensible, and we plan to provide more formats.

B Using Semantic Patches

The ocplib-sempatch library was inspired the Semantic Patches from by Coccinelle [START_REF]Laboratoire d'Informatique de Paris 6 Julia Lawall[END_REF]. It provides a simple text DSL to describe patterns on OCaml syntax in a patch-like style, and these patterns can be used to locate these patterns in source files. For that, it uses a regular expression engine operating on the OCaml AST -the parsing AST or, when needed and possible, the typed AST.

In ocp-lint, we use this library to allow the user to provide its own OCaml patterns to recognize. For example, the following patch will make ocp-lint raise a warning, proposing to replace a if-then-else constructs with identical expressions in both branches by a simple sequence, ignoring the result of the condition: @ConstantIf expressions : cond, e1, e2 when: '' e1 = e2'' ''' -if cond then e1 else e2 + ignore (cond:bool) ; e1 '''

We were able to use these semantic patches to express most of the patterns recognized by ocamllint.

C Extensibility

It is difficult to forecast all the checks that users will want to apply on their code. We implemented a large set of checks, that can be configured on a user basis, or per project, and semantic patches can be used to add more patterns to detect. Nevertheless, we thought it would be useful to allow users to define their own complex checks, and for that, we provided a simple way to develop plugins and link them at runtime.

We sketch here the development of a simple plugin. First, we create a Plugin to which analyses will be attached:

module Plugin = Lint_plugin_api .
MakePlugin ( struct let name = " Text Plugin " let short_name = " plugin_text " let details = " A plugin ... " end )

Then, we attach a first analysis to this plugin:

module Linter = Plugin . MakeLint ( struct let name = " Detect use of .. " let version = 1 let short_name = " not_that_char " let details = " Detect ... " end )

We can now define and attach the warnings that will be raised. For brievity, we just display one definition: let w_non_ascii_char = Linter . new_warning ~id :1 ~short_name : " non_ascii_char " ~msg : " Non -ASCII char $char used "

In the code, we usually prefer the use of symbolic warnings, so we define them and provide a translation function: We can now define our analysis. We can use several reporting functions defined by the MakeWarnings functor: Finally, we declare that the analysis should be carried out on source files: module MainSRC = Linter .

MakeInputML ( struct let main source = check_file source end )

We have predefined several kinds of inputs for analysis:

• All files: the input is the list of files to check

• Source file: the input is the name of a source file to check

• Tokens: the input is the list of tokens from the OCaml lexer

• Parsetree: the input is the standard AST

• Typedtree: the input is the typed AST, from the .cmt file Currently, we have created one plugin per input, that gathers together all the analyses on that input. However, we expect users to define their own plugins, mixing analyses on the different inputs, as better suited for their checks.

Finally, we are now working on custom inputs, i.e. an input that can be defined by the user, and then used by multiple analyses. A typical example of use is the ocp-lint-pluginparsing plugin, that includes a full OCaml 4.03 parser, that we have modified to generate an AST closer to the real input. Currently, this plugin is defined as one analysis, taking a source and applying many checks on the AST (for example, to warn for extra parenthesis). However, we would like to define the analysis separately, while still parsing only once the file with the new parser, defined as a user custom input.

D Conclusion

When designing ocp-lint, our goal was to use it on our own Github projects, to check pullrequests both from the project developers and from external contributors. We plan to apply it soon to all our projects, once most of the analyses we need are implemented.

Although we currently use a non-optimized approach in the implementation of the analyses (different checks are often done in different analyses, while they could be done in the same iteration on the AST), performance is good enough for its purpose. For example, running sequentially all the current 30 analysis of our 6 plugins, we get the following performances: We also took internationnalization into account in the design: the message associated with each warning is a simple string, that will be customized in the future for different languages.

The project sources are hosted on Github, and an OPAM package should be available soon.

  type warning = | NonAsciiChar of string | .. module Warnings = Linter . MakeWarnings ( struct type t = warning let to_warning = function | NonAsciiChar s -> w_non_ascii_char , [ " char " , s ] | .. end )

  let check_line lnum line file = .. Warnings . r e p o r t _ f i l e _ l i n e _ c o l file lnum i ( NonAsciiChar ( Printf . sprintf " \\%03 d " c ) ) .. let check_file file = FileString . iteri_lines ( fun lnum line -> check_line lnum line file ) file

Diffusion

ocp-lint is being developed on Github, as part of Typerex linting tools, and distributed under GPLv3, with several plugins and many useful checks: http://github.com/OCamlPro/ typerex-lint


Benjamin Canou
email: benjamin.canou@ocamlpro.com

Grégoire Henry
email: gregoire.henry@ocamlpro.com

C ¸agdaş Bozman
email: cagdas.bozman@ocamlpro.comfabrice.lefessant@inria.fr

Fabrice Le Fessant

Learn OCaml An Online Learning Center for OCaml

We present Learn OCaml, a Web application that packs a set of learning activities for people who want to learn OCaml. It includes an integrated and reworked version of the venerable Try OCaml, and an exercise environment with automated grading derived from the one developed for the OCaml MOOC. It works entirely in the browser, the server being used for storing static files and synchronizing between different devices. A special effort has been made to make it usable on tablets, and even mobiles. A main public instance will be hosted at OCamlPro, but the project is open-source, and universities can host their own version on site. We will also provide a public repository for teachers to contribute lessons and exercises.

Introduction

Learn OCaml is a new Web platform than combines several learning tools. It is targeted at a broad audience: at curious people to get a quick taste of OCaml, at newcomers to learn the language, at casual users to refresh or improve their skills, and at regular OCaml programmers to learn about and try new features.

Building on several past experiments by OCamlPro, it currently features a sophisticated toplevel, an interface for browsing quick tutorials and another for reading longer lessons, and an environment for solving exercises with automatic grading. We are open to other features proposals, or even contributions, as the platform is free software placed under the GNU Affero GPL.

The next three sections present the main learning activities that have already been developed, giving for each a quick description, a bit of background, and some example uses. The last section discusses our (open) ambitions for this project. Several commented views of the interface are given in appendix.

The Toplevel Activity

A first learning activity, also used as a component in the other activities, is an OCaml toplevel. This toplevel introduces several new features, compared to the demonstration shipped with js of ocaml or the one of Try OCaml.

To run OCaml code, we use a bytecode executable that runs a custom toplevel loop. We compile it to JavaScript using the toplevel-compatible compiling mode of js of ocaml.

We wrote a richer version of the Toploop module that allows us to trap the various outputs of the compiler. This way, we can present them to the user using different styles for better readability, colorize code and toplevel answers, and highlight the locations of warnings and errors.

The toplevel loop also traps the standard output channels of the evaluated code, so that it may prevent display flooding. After a piece of code exceeds some predefined amount of output, it is buffered and retained until the user selects to either drop or show it from a dialog box.

A similar protection is also provided against infinite loops. When the run time of a phrase exceeds some given number of seconds, the user is asked if she wants to give it a few more. Otherwise, it is killed after a countdown expires (so that the user does not let some hidden tab consume CPU time).

Figure 2 in the appendix shows several of these features.

For several reasons, including the flooding and looping prevention mechanisms, the toplevel does not run directly in the page, but in a separate process, using the standard Web Worker API. This choice has the important advantage of making the interface reactive even when code is running. But this isolation prevents the code to interact with the user directly. For that, the toplevel environment is enriched with callbacks to display raw HTML fragments, sent from the webworker, in the toplevel console. It would be possible, for instance, to implement a version of Graphics over this mechanism.

Also thanks to this structure, it could be fairly easy to run toplevel sessions on a remote server, for instance to target low power machines. It could also be possible to package Learn OCaml in an application, and then to use a standard (bytecode or native) toplevel executable.

The Tutorial Activity

Learn OCaml includes a new implementation of Try OCaml (an interactive OCaml tutorial in the browser developed by OCamlPro) that uses the new toplevel. Try OCaml lets the user choose a tutorial from a list. Each tutorial consists in a series of screens, containing some OCaml phrases with explanations and comments. The user can click on each phrase to see it interpreted in the side toplevel, and she can also try her own phrases.

This interface is not meant to replace a book. It is designed to present small tutorials, small chunk by small chunk. Currently, we use it for two main use cases. A first set of short tutorials covers the core features of OCaml, and serve as a quick tour of the language for curious users of other programming languages. A second set of tutorials is targeted at OCaml programmers, and cover the changes introduced by new releases of OCaml.

One of the difference with the previous implementation is that it is designed using the responsive features of modern browsers. Its interface will adapt itself automatically to the screen size and device capabilities of the machine on which it runs.

As seen in Figure 3 in the appendix, in the desktop version, the screen is split between the different parts. In the mobile version, some parts require a touch to be shown or hidden.

The Exercise Activity

One of the key elements of the OCaml MOOC, which really stood out compared to other programming MOOCs according to our end of MOOC survey, was its exercise environment. First, students did not have to install anything to enjoy learning OCaml, since the environment runs entirely inside the Web browser. So the effort needed to go from just watching the first videos to doing the first exercises was no more than a click. Once attracted, we observed in our statistics that almost all of the learners who completed the first exercises were immediately addicted to the system, and stayed until he end. Most of them even tried to get 100 points on all the exercises, even the optional ones. This is explained by the automated grader, which also runs inside the browser, and is thus able to give quick feedback. Solving an exercise becomes a game of incrementally satisfying the tests performed by the grader, until the report is all green. It was, actually, the starting point of the development of Learn OCaml, a platform to allow people to learn OCaml in such an entertaining fashion, out of the constraints of a MOOC.

Figure 1 in appendix shows the exercise environment in both mobile and desktop sizes. The student is first presented a list of exercises to choose from, which also indicates the status of the exercise (percentage of completion). This information is stored in the local storage of the browser, and can optionally be synchronized with a server. When she selects an exercise, she is given a tabbed interface, with a tab containing the text of the exercise, a tab with an OCaml editor to write her solution, a tab to display the grading report, and a toplevel initialized with the exercise prelude.

The text editor is based on the ACE JavaScript component. We developed an OCaml mode for it that uses ocpindent to bring both syntax coloring and indentation. Indentation is mandatorily performed, in almost real time. This way, newcomers learn the OCaml formatting style without even thinking about it. This is an experimental behaviour that we tried on the MOOC learners, which turned out successful so we kept it in Learn OCaml.

The grading mechanism has been developed especially for this environment. It has been described in [START_REF] Canou | Sous le capot du mooc ocaml[END_REF] (in French, an English article is in preparation). It would be far too long and off scope to present the details, but here are the main design choices. The grading mechanism is strongly tied to OCaml, which allows the grader to manipulate the values and types of the learner's code directly in a type-safe manner. It has been tested and refined on the 50 exercises of the OCaml MOOC, covering most of the functional and imperative features of OCaml, and an introduction to modules and functors. Graders are reasonably terse (around 3 times the size of solutions), and the type-aware design give great confidence in their strength. Once a grader passes the offline grader simulator on a reference solution, the teacher knows that the only things that may remain to be fixed are the exercise text and the random sampling bias.

Conclusion

We put lots of effort in developing this platform. The goal is to make the best Web learning center possible about OCaml. For that, lots of work remain, to improve and polish the user experience.

An important matter is the one of pedagogical content. For this, we will open a public repository for exercises and tutorials, that will be initially populated with contents from the MOOC and OCamlPro's own material. This talk will be the opportunity to gather volunteering and advice on that matter, especially from people with experience in teaching OCaml.

At the time of this talk, the code should be hosted on Github, distributed under an Affero GPL license. A main public instance of Learn OCaml will be hosted at OCamlPro, but universities and schools will be able to install the open-source project locally to host their own version of the site. We will also provide a public repository for teachers to share lessons and exercices, although it is not yet clear whether how to prevent students from accessing the solutions of the contributed exercices. Locations in errors and warnings are numbered, the corresponding code is underlined.

For advanced examples, is is possible to output HTML or SVG in a console block.

The toplevel detects a flood on stdout and asks the user if she wants to hide or display it.

If a run is too long, the user is asked for a few more seconds. Without answer, the toplevel is killed.

When interrupted, the toplevel is restarted. The user can retry its phrase by accessing the history.

  Toplevel phrases are separated, different styles help identify the different kinds of output.

Figure 2 :

 2 Figure 2: The toplevel interface (tablet size)

Appendix: Screen Captures

The toplevel tab, which is loaded with the exercise prelude.

The report tab, whose section are smartly unfolded to show the first failed test.

The list of exercises students can brag about when all green.

The desktop version always shows the editor pulled out of its tab on the left. Here, the exercise text tab is shown.