- Ruby is a mature language with a lot of libraries and a lot of code written in it.
- Ruby is a dynamically typed language, which means that the type of a variable is not known until runtime or is known only in the mind of the developer.
- What ruby lacks is a type system, which is a set of rules that define what types are allowed in a program.
- Solutions like
Sorbet
andRBS
exist, but they are not part of the language itself and are not widely used. They have their pros and cons. - The problem with
RBS
is that you have to write a lot of boilerplate code to define types that are only a hint for you favorite IDE. - The problem with
Sorbet
is that it's not very intuitive and you have to take time to learn and get used to the way it works.
The RubyTypeSystem project aims to create an intuitive and easy-to-use type system for Ruby. The main ideas behind this project are:
-
Intuitive Types: The types used in this system come from the Ruby standard library. This makes the system intuitive for developers who are already familiar with Ruby.
-
Type Inference: The system will take care of most of the typing through type inference. This reduces the amount of type annotations that developers need to write, making the code cleaner and easier to read.
-
Method Typing: Despite the type inference, methods will still require typing. This ensures that the behavior of methods is clear and predictable.
-
New Keywords: The system introduces new keywords to describe interfaces, abstract classes, union types, and types in general. These keywords extend the Ruby language with powerful features for static typing.
some_int: Integer = 1
some_float = some_method_that_returns_float # The compiler assumes a Float type
some_int + some_float # CompileTimeError
def some_method(a: Integer, b: Integer): Integer
a + b
end
A more experienced developer would argue "But how are we going to introduce default values for parameters" and that is a great question. Let's take the following ruby code as an example:
def a(some_val = 1)
some_val + 1
end
def b(key: 'key', value: 'value')
"#{key}: #{value}"
end
In this example we have two methods, both have parameters with default values, but the first method has a positional parameter and the second method has keyword parameters. In case of positional parameters the following code is equivalent:
def a(some_val: Integer = 1): Integer
some_val + 1
end
In case of keyword parameters the following code is equivalent:
def b([key: String]: 'key', [value: String]: 'value'): String # this approach is up for debate, you can propose a better way in the issues section
"#{key}: #{value}"
end
- The system will be implemented as a compiler that compiles Ruby code down to a single expression.
- The compiler will be written in Ruby.
- The compiler will be written in a way that allows it to be used as a library and a CLI.
- All ruby code will be valid RubyTypeSystem code.
- All type annotations will be compiled down to runtime type checks.
- The compiler will be able to compile Ruby code without type annotations(will print a lot of warnings).
- Create a compressor, that compresses the source codes down to a single file
- Create a lexer
- Create a parser that produces an AST
- Create a type checker
- Create a type inference engine
- Create a compiler
- Create an Optimizer to optimize the compiled code down to a single expression