https://github.com/tristanpenman/ruby-type-checking
Random experiments in Ruby Type Checking 🔬
https://github.com/tristanpenman/ruby-type-checking
metaprogramming ruby sorbet type-checking
Last synced: 6 months ago
JSON representation
Random experiments in Ruby Type Checking 🔬
- Host: GitHub
- URL: https://github.com/tristanpenman/ruby-type-checking
- Owner: tristanpenman
- Created: 2022-12-26T11:03:54.000Z (almost 3 years ago)
- Default Branch: master
- Last Pushed: 2024-03-26T21:08:54.000Z (over 1 year ago)
- Last Synced: 2024-10-19T10:23:53.621Z (12 months ago)
- Topics: metaprogramming, ruby, sorbet, type-checking
- Language: Ruby
- Homepage:
- Size: 32.2 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Ruby Type Checking
This repo contains some experimental code that I've written while learning about type checking in Ruby. The aim of these experiments has been to better understand Ruby, and to reason about how runtime type checking works in [Sorbet](https://sorbet.org).
My findings have been written up as a series of posts, _Roll Your Own Ruby Type Checking_, on my blog:
* [Part 1](https://tristanpenman.com/blog/posts/2022/12/26/roll-your-own-ruby-type-checking-part-1/)
* [Part 2](https://tristanpenman.com/blog/posts/2023/05/13/roll-your-own-ruby-type-checking-part-2/)
* [Part 3](https://tristanpenman.com/blog/posts/2023/05/20/roll-your-own-ruby-type-checking-part-3/)The code examples here roughly follow the structure of these posts.
## Part 1 - Annotations, Hooks and Type Checking
The code for part 1 can be found in [part-1.rb](part-1.rb). This code demonstrates a technique for implementing method annotations in Ruby, allowing a method to be annotated with hooks that are run before and after the method body. This is then adapted for type-checking.
This code can be run from the command line with no arguments:
ruby part-1.rb
Current output is expected to look like this:
Example 1:
test, test, testExample 2:
before: ["test", 3], {:separator=>", "}
after: test, test, test
test, test, testExample 3:
before: ["test", 3], {:separator=>", "}
after: test, test, test
test, test, testExample 4:
testtesttestError: Invalid type for arg 1; expected: Numeric
## Part 2 - Optional and Additional Parameters
The code for part 2 can be found in [part-2.rb](part-2.rb). This code addresses some of the limitations of the type checker implemented in part 1, while also making it more robust.
This code can be run from the command line with no arguments:
ruby part-2.rb
There are quite a few examples in part 2. The beginning of the output should look like this:
Example 1a:
[:req, :a]
[:opt, :b]
[:rest, :c]
[:keyreq, :d]
[:key, :e]
[:keyrest, :f]Example 1b:
[:req, :a]
[:opt, :b]
[:rest, :c]
[:keyreq, :d]
[:key, :e]
[:keyrest, :f]Example 2a:
["a", 1]
["b", 2]
["c", [3, 4]]
["d", 5]
["e", 6]
["f", {:f=>7, :g=>8}]... SNIP! ...
## Part 3 - Breaking Sorbet
While experimenting with my own type checker, I found that there were some fundamental limitations on how runtime type checking can be performed in Ruby. Specifically, it's not possible to verify the types for parameters with default arguments, when those arguments are omitted.
This has been demonstrated using Sorbet in [part-3.rb](part-3.rb). You'll need to install the `sorbet-runtime` gem to run this code.
To illustrate how this differs from Python, [part-3.py](part-3.py) has also been included. This highlights the fact that, no matter which approach you choose, you end up with surprising behaviour.
## License
These examples are in the public domain.