Skip to content

Instantly share code, notes, and snippets.

@robblanco robblanco/gsoc.md
Last active Aug 23, 2016

Embed
What would you like to do?
StrongRuby GSoC work product

The initial StrongRuby proposal for Google Summer of Code 2016 was conceived as a contribution to the canonical Ruby interpreter, MRI, and potentially a close collaboration with the ruby-core group of developers, given the growing interesting in the Ruby community to explore the addition of some form of static typing to the language. However, between the submission of the proposal and the start of the project it became clear that the general idea had to be rethought from the ground up.

The approach favored by ruby-core would be one that has no impact on the syntax of the language, based on the kind of type inference pioneered by soft typing and possibly assisted by specially formatted comments similar to RDoc. The closing of the representative issue #9999, the most recent of several to discuss the possible addition of explicit types to the language, reflects this stance.

Nevertheless, the survey of research and programming languages conducted in preparation for the project showed the opposite trend. In the scientific literature, soft typing (which does not rely on type annotations) attracted interest for a brief period in the early 1990s, centered around much more lightweight languages like Scheme. Nowadays, gradual typing is the focus of research, and the current wave of languages and dialects with optional types is built upon these foundations.

In light of these considerations, we determined a successful and modern typed Ruby needs to follow in the footsteps of similar languages with similar trajectories, of which perhaps the most popular example is TypeScript (for JavaScript). In like fashion, we resolved to define a gradually typed dialect of Ruby, the titular StrongRuby, and in the process the project became less one of pure implementation with rather clear goals, and consequently design and prototyping rose to greater relevance.

Making the project independent from MRI did complicate planning in two senses: firstly, a new route had to be traced; and secondly, this new route was approximate and exhibited more degrees of freedom, the constraints of which were not clear from the outset. On the upside (intellectual stimulation notwithstanding), it gave us a chance to make informed and unconstrained decisions, defining a natural extension of the language and the tools that will support it across all of its implementations: MRI, JRuby, Rubinius, and others. Moreover, we could do this in pure Ruby and harness some of the excellent tools and libraries that are one of the landmarks of the language.

For the duration of the program, we have focused on two tools. The first is parser, extended with a new “version” of Ruby based on the grammar of Ruby 2.3 and extended with a type sub-grammar that furnishes optional type annotations at various points in the language. The second is RuboCop, a static analyzer enriched with a new category of checks or “cops”, and which after several prototypes proved to offer a suitable execution model and framework with which to program the checker.

Originally, we planned the (MRI-based) type checker to cover inheritance, and possibly some form of duck typing. The StrongRuby type checker supports inheritance, but at the moment does not consider any form of duck typing and the more sophisticated dynamic features of the language. In addition, some simplifications were necessary, in that currently a fairly interesting subset of Ruby is typechecked, but full support still requires some (ongoing) work. In practice, this means that typechecking general programs can result in spurious type errors for those parts of the program making use of features for which no typing analysis is currently performed.

This strategy of depth over breadth arose organically from the observation that a minimalistic underlying type theory (here used in the loosest sense) is too weak to support the entirety of the gradually typed language (StrongRuby) and its interface with the dynamically typed (Ruby). For example, on the one hand, simple types cannot accommodate even rather basic Ruby expressions, and concepts such as union types need to be introduced earlier than expected. On the other hand, all nontrivial programs rely on the foundations of the language, the core modules and standard library, for which no explicit type information avails in Ruby itself: therefore, the ability to describe type signatures for plain Ruby modules and classes becomes indispensable in order to typecheck any significant codebase. We have prioritized these fundamental issues and the big picture over exhaustive typechecking for smaller features of the language, with all the design overhead that involves. Such issues were not clearly envisioned while planning for a modification of MRI, but quickly raised to prominence and continued to inform and influence the development of the system as it stands today… as they will in the immediate future.

Currently, working with the core of StrongRuby consistently requires local builds of the parser and rubocop gems. Our mid-term goal is to integrate these in the associated gems, either as part of their official releases or as add-ons, once support is near completion.

The following steps can be followed to build and use the “GSoC version” of the type checker.

  1. Clone the strongruby branch of parser's fork at https://github.com/strongruby/parser.

  2. If versioning is an issue, bump the gem version in lib/parser/version.rb.

  3. Build and install the gem (gem build followed by gem install).

  4. Clone the strongruby branch of rubocop's fork at https://github.com/strongruby/rubocop/.

  5. If versioning is an issue, bump the gem version in lib/rubocop/version.rb. Depending on (2), modify the runtime dependency on parser in rubocop.gemspec.

  6. Build and install the gem (gem build followed by gem install).

In this version of the gem, the cop TypeCheck/TypeChecker can be used to perform type checking on StrongRuby files, for example, using the command rubocop --only TypeCheck/TypeChecker in the directory or choice, or specifying the list of files and directories to verify. It is important that RuboCop load the StrongRuby parser, otherwise files with type annotations won't be understood. (In this build, StrongRuby is temporarily mocked in RuboCop by pseudo-version 2.4, as of yet unreleased in Ruby.) This “Ruby” version can be simply written inside the .ruby-version file in the directory of interest. Alternatively, a RuboCop configuration file can be given in .rubocop.yml.

In order to run RuboCop's tests, it will be necessary to make a similar adjustment to the version in lib/rubocop/rspec/cop_helper.rb. However, it must be noted that a small number of tests rely on a certain version to run correctly, and this may cause some spurious errors, dependent exclusively on the version used by the helper.

The type checker is currently capable of verifying many relatively simple programs, especially those without dependencies spread among various files and modules. More complex programs will generate typing errors corresponding not only to violations of the typing discipline, but also to some unsupported language features.

Currently, there is ongoing work on several important features that are not yet stable enough to be committed to the main repositories. The following “GSoC branches” reflect the stable state of the various main parts of the project at the time of the present evaluation.

Work on the StrongRuby parser: https://github.com/strongruby/parser/commits/gsoc?author=robblanco

Work on the StrongRuby type checker: https://github.com/strongruby/rubocop/commits/gsoc?author=robblanco

Additionally, there is a scaffold for the eventual composition of the full Ruby gem, incorporating all necessary parts: https://github.com/strongruby/strongruby/commits/gsoc?author=robblanco

And, finally, a website that will be used to document the stable versions of the language, still in preparation: https://github.com/strongruby/strongruby.github.io

There remains work to be done, and development will continue immediately after the end of GSoC. Significant tasks include a stable type signature of the core and standard library of the language, and completion of the type checking algorithm for the full language. It is expected that significant “untyped” code bases should typecheck without issue (for this, the entire RuboCop source code tree has been used as a running example; there remain a few categories of common errors that will be addressed presently). Beyond that, collections of behaviors like the Ruby Spec suite are good targets against which to verify a complete implementation.

In the project proposal, we considered the possibility that an eminently exploratory effort like this may run off course, and made some way towards addressing difficulties during development. Due to the necessity to significantly alter the scope, as well as the difficulties of planning during the initial weeks, the results have adopted a form that is substantially different from what was originally imagined. Nevertheless, in general terms the general concepts that we intended to illustrate, albeit in a somewhat simplified model, have proven sound, and we have laid the framework on which we can be reasonably confident that, in a few months' time, a fuller and potentially useful and transformative form of Ruby can be built.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.