typist
typist
is a gem that allows you to define Algebraic Data Types (ADTs) in Ruby.
For a tutorial on ADTs, I recommend Learn You a Haskell's tutorial.
Features:
- A rich DSL that allows for idiomatic defintions of the data types
- Pattern matching
- Runtime support for incomplete pattern matches
- Class-load time support for invalid pattern matches
Planned Improvements:
- Type classes
- Optional runtime type checking
Installation
From the command line:
$ gem install typist
From a Gemfile:
gem 'typist'
Usage
To define a data type, first extend the Typist
module in a top-level statement, or in the module in which you'd like your data type defined.
For example, to create a new data type in the Test
module:
module Test
extend Typist
...
end
Once Typist
has been extended, the data
function will define a data type.
The following defines a new data type called Tree
in the Test
module:
module Test
extend Typist
data :Tree do
...
end
end
Type constructors may be defined using the constructor
function.
module Test
extend Typist
data :Tree do
constructor :Leaf
constructor :Node, :value, :left, :right
end
end
Now, Tree::Leaf
and Tree::Node
are defined.
The arguments that come after the constructor name are the instance variables -- accessors are defined for each of them.
To create a new Leaf, run Tree.leaf
.
To create a new Node, run Tree.node(:value => val, :right => Tree.leaf, :left => Tree.leaf)
.
The DSL also allows the user to define and pattern match in functions.
The func
method in the context of a data type declares a new function.
For example:
module Test
extend Typist
data :Tree do
constructor :Leaf
constructor :Node, :value, :left, :right
func :contains? do
match Tree::Leaf do |element|
false
end
match Tree::Node do |element|
case value <=> element
when -1
left.contains?(element)
when 1
right.contains?(element)
else
true
end
end
end
end
end
This defines #contains?
method on Tree::Node
and Tree::Leaf
.
Finally, you can define "class methods" upon the data type using the data_func
function.
For example:
module Test
extend Typist
data :Tree do
constructor :Leaf
constructor :Node, :value, :left, :right
data_func :singleton do |value|
Tree.node(:value => value, :left => Tree.leaf, :right => Tree.leaf)
end
func :contains? do
match Tree::Leaf do |element|
false
end
match Tree::Node do |element|
case value <=> element
when -1
left.contains?(element)
when 1
right.contains?(element)
else
true
end
end
end
end
end
Example usage:
leaf = Test::Tree.leaf
node = Test::Tree.singleton('a')
leaf.contains?('a')
# => false
node.contains?('a')
# => true
Contributing
- Fork the repository
- Create a branch
- Add tests
- Commit your changes
- Push the branch
- DO NOT bump the version
- Submit a Pull Request