[ANN] MLTypes: ML-style qualified unions for ruby

L

Logan Capaldo

I've always liked ML-style languages for declaring new types, and I
thought hey, let me try to do that in ruby, its already got a case
statement that reminds me of ML. So on with the examples:

% cat example.rb
require 'mltypes'

deftype :List do
:Cons.of( :first => :Object, :rest => :List) | :End
end

deftype :Tree do
:Leaf.of:)data => :Object) | :Branch.of( :data
=> :Object, :left => :Tree, :right => :Tree )
end

def print_list(list)
case list.tag
when :Cons
puts list.first
print_list(list.rest)
when :End
end
end


def print_tree_inorder(tree)
case tree.tag
when :Leaf
puts tree.data
when :Branch
print_tree_inorder(tree.left)
puts tree.data
print_tree_inorder(tree.right)
end
end

ls = List.Cons:)first => 1, :rest => List.End)
ls = List.Cons( :first => "Hello", :rest => ls )
print_list ls

tree = Tree.Branch( :data => 2, :left => Tree.Leaf( :data =>
1 ), :right => Tree.Leaf( :data => 3 ))

print_tree_inorder(tree)

__END__

Not bad huh?

The other interesting thing to note (for me anyway is)

deftype :MyBoolean do
:Yes | :No # bar works here
end

:maybe | :so # not here though
NoMethodError: undefined method `|' for :maybe:Symbol
from (irb):6

So it shouldn't mess up any existing stuff that relies on Symbol not
having |.
OTOH, its kind of heavy -handed (ie if you defined | for something
else it will mess stuff up completely. Just keep that in mind)

Now here is the code that does all this fun.

% cat mltypes.rb
module MLTypes
class Alternation
def initialize(alts = [])
@alts = alts
end
def |(other)
@alts << other
self
end

def alternatives
@alts
end
end
class TaggedTuple
def initialize(tag, pairs)
@tag = tag
@pairs = pairs
end

def |(other)
Alternation.new([self, other])
end

def tag
@tag
end

def pairs
@pairs
end
end
end
def deftype(name)
res = nil
begin
Symbol.class_eval {
define_method:)"|") do |other|
MLTypes::Alternation.new([self, other])
end

define_method:)of) do |pairs|
MLTypes::TaggedTuple.new(self, pairs)
end
}
res = yield
ensure
Symbol.class_eval {
remove_method:)"|") rescue nil
remove_method:)of) rescue nil
}

end

Object.const_set(name.to_s, Class.new)
klass = Object.const_get name

klass.class_eval {
define_method:)tag) do
@tag
end
m = instance_method:)method_missing)

define_method:)method_missing) do |*args|
if not @state.nil? and @state.has_key?(args[0])
@state[args[0]]
else
m.bind(self).call(*args)
end
end

res.alternatives.each do |enum|
obj = klass.new
metaklass = (class << self; self; end)
if enum.kind_of?(MLTypes::TaggedTuple)
metaklass.class_eval {
define_method(enum.tag) { |
args|
state = {}
enum.pairs.keys.each
do |key|
raise
TypeError, "Expected #{enum.pairs[key]} got #{args[key].class}"
unless args[key].kind_of? Object.const_get(enum.pairs[key])
state[key] =
args[key]
end
res =
klass.class_eval { new }
res.instance_eval
{ @state = state; @tag = enum.tag }
res
}
}
else # regular enum
obj.instance_eval { @tag = enum }
metaklass.class_eval {
define_method(enum) do
obj
end
}
end
end

class << self
private :new
end
}

end

__END__
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top