Project

typist

0.0
No commit activity in last 3 years
No release in over 3 years
Algebraic data types for Ruby
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

 Project Readme

typist

Gem Version Build Status Code Climate

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

  1. Fork the repository
  2. Create a branch
  3. Add tests
  4. Commit your changes
  5. Push the branch
  6. DO NOT bump the version
  7. Submit a Pull Request