[ANN] (Real) Primitive Ruby Generics support

Hi all,

I have been hacking away to create a simple library that adds
generics-like qualities to ruby. At the moment you can define methods
with type-matches, defaults if no match and different number of
argument matches. Currently thinking about adding pattern-matching
support from eigenclass blog. Depends on ruby facets.

Usage examples (Tests) are at the bottom.
Any suggestions, comments, flames :wink: ?

thanks,
Isaac Devine
P.S. BSD licensed. (don't know about the bit from why's guide though...)

--------------generics.rb--------------
# This file contains all you need to add generics to any ruby class :slight_smile:
# ie.
# class Sample
# include Generics
# generic_method :name, Types do
# CODE
# end
## Default case
# generic_method :name do
# CODE
# end
# end
require 'mega/inheritor'

# This Object stuff from Why's Metaprogramming guide
class Object
聽聽聽# The hidden singleton lurks behind everyone
聽聽聽def metaclass; class << self; self; end; end
聽聽聽def meta_eval &blk; metaclass.instance_eval &blk; end

聽聽聽# Adds methods to a metaclass
聽聽聽def meta_def name, &blk
聽聽聽聽聽meta_eval { define_method name, &blk }
聽聽聽end

聽聽聽# Defines an instance method within a class
聽聽聽def class_def name, &blk
聽聽聽聽聽class_eval { define_method name, &blk }
聽聽聽end
end
# End Why's Stuff.

module Generics
聽聽class_inherit do
聽聽# Get a metaclass for this class
聽聽def metaclass; class << self; self; end; end

聽聽聽# metaprogramming code for generic_method
聽聽def generic_method(method_name, *types, &blk )
聽聽聽聽# have a class instance hash which holds the following:
聽聽聽聽# { :method_name => { type_name => block, type_name => block } }
聽聽聽聽# initialize it here...
聽聽聽聽class_eval {
聽聽聽聽聽聽# define the class generic_signatures if they don't exist
聽聽聽聽聽聽@generic_signatures = Hash.new if not
defined?(@generic_signatures) # define the generic method's signatures
if they don't exist @generic_signatures[method_name] = Hash.new unless
@generic_signatures.has_key?(method_name)
聽聽聽聽聽聽def self.generic_signatures
聽聽聽聽聽聽聽聽return @generic_signatures
聽聽聽聽聽聽end
聽聽聽聽}
聽聽聽聽
聽聽聽聽# check to see if we are the default
聽聽聽聽if types.empty?
聽聽聽聽聽聽class_eval {
聽聽聽聽聽聽聽聽@generic_signatures[method_name].default = blk
聽聽聽聽聽聽}
聽聽聽聽else # got a typelist?
聽聽聽聽聽聽# create the type "string"
聽聽聽聽聽聽specific_method_name = types.join("_").to_sym
聽聽聽聽聽聽class_eval {
聽聽聽聽聽聽聽聽@generic_signatures[method_name][specific_method_name] = blk
聽聽聽聽聽聽}
聽聽聽聽end
聽聽聽聽
聽聽聽聽# define the class method that does the dispatch to
聽聽聽聽# the appropiate block.
聽聽聽聽class_def(method_name) do |*args|
聽聽聽聽聽聽type_sig_arr = args.collect { |a| a.class }
聽聽聽聽聽聽type_sig = type_sig_arr.join('_').to_sym
聽聽聽聽聽聽self.class.generic_signatures[method_name][type_sig].call(*args)
聽聽聽聽end
聽聽end
聽聽聽聽
聽聽end
end

class Test
include Generics
聽聽generic_method :get, String do |arg|
聽聽聽聽puts "In String... -- #{arg}"
聽聽end
聽聽
聽聽generic_method :get, Fixnum do |arg|
聽聽聽聽puts "In Fixnum... -- #{arg}"
聽聽end
end

class Test2
include Generics
聽聽聽generic_method :two_args, String, Fixnum do |arg1, arg2|
聽聽聽聽聽puts "got a String and Fixnum"
聽聽聽end
聽聽聽generic_method :two_args, String, String do |arg1, arg2|
聽聽聽聽聽puts "got two Strings"
聽聽聽end
end
# does having a method that accepts two different numbers
# of arguments work?
class TestVariable
聽聽include Generics
聽聽聽聽generic_method :test_method, String do |arg1|
聽聽聽聽聽聽puts "single argument"
聽聽聽聽end
聽聽聽聽
聽聽聽聽generic_method :test_method, String, Fixnum do |arg1,arg2|
聽聽聽聽聽聽puts "two arguments"
聽聽聽聽end
end

class TestDefault
聽聽include Generics
聽聽
聽聽generic_method :test, String do |arg|
聽聽聽聽puts "in String!"
聽聽end
聽聽
聽聽generic_method :test do |arg|
聽聽聽聽puts "The rest!"
聽聽end
end

Hi --

路路路

On Tue, 27 Dec 2005, Isaac Devine wrote:

Hi all,

I have been hacking away to create a simple library that adds
generics-like qualities to ruby. At the moment you can define methods
with type-matches, defaults if no match and different number of
argument matches. Currently thinking about adding pattern-matching
support from eigenclass blog. Depends on ruby facets.

Usage examples (Tests) are at the bottom.
Any suggestions, comments, flames :wink: ?

Call classes classes, not types :slight_smile:

Call singleton classes singleton classes, not metaclasses or
eigenclasses :slight_smile:

David

--
David A. Black
dblack@wobblini.net

"Ruby for Rails", from Manning Publications, coming April 2006!

Isaac Devine ha scritto:

Hi all,

I have been hacking away to create a simple library that adds
generics-like qualities to ruby. At the moment you can define methods
with type-matches, defaults if no match and different number of
argument matches. Currently thinking about adding pattern-matching
support from eigenclass blog. Depends on ruby facets.

Usage examples (Tests) are at the bottom.
Any suggestions, comments, flames :wink: ?

thanks,
Isaac Devine

I think you could be intererested in the StrongTyping module
http://raa.ruby-lang.org/project/strongtyping/

Isaac Devine wrote:

I have been hacking away to create a simple library that adds
generics-like qualities to ruby. At the moment you can define methods
with type-matches, defaults if no match and different number of
argument matches.

Been there, done that:

http://ruby-contract.rubyforge.org/

New ideas are very welcome.

路路路

--
http://flgr.0x42.net/

Just to be clear, the original post wasn't inventing terminology, it was referring to a blog:

http://eigenclass.org/

James Edward Gray II

路路路

On Dec 27, 2005, at 4:46 AM, dblack@wobblini.net wrote:

On Tue, 27 Dec 2005, Isaac Devine wrote:

eigenclass blog

Call singleton classes singleton classes, not metaclasses or
eigenclasses :slight_smile:

Thanks! I've quickly looked at the ruby-doc for that. It's seems to
only be able to specific "contracts" for classes, with method
signatures as a subset. What my goal for generics is to be able to
choose what code to execute based on method/class parameter types.
ie.
converting :
def foo(arg)
if arg.kind_of? String
  puts "it is a string! reverse it! #{arg.reverse}"
elsif arg.kind_of? Fixnum
  puts "Fixnum! double it! #{2*arg}"
end
end

into:

generic_method :foo, String do |arg|
  puts "it is a string! reverse it! #{arg.reverse}"
end

generic_method :foo, Fixnum do |arg|
  puts "Fixnum! double it! #{2*arg}"
end

In the future I would like to be able to extend this to classes as
well. One implication of this we will be the ability to get rid of
"adapter" classes when joining heirachys. One example would be adding
support to reading from a String when a class can only read from a File.

ie.
generic_class :SomeClass, String do
  def getline
...
end

Another wish is for pattern matching:
  generic_method :foo, :a, :b :a do ...
where wherever :a occurs it must be the same type so:
  foo 4 "a" 4 passes
but
  foo "a" 4 4 fails.

Looking at the rdoc some code in that could be very helpful - such as
Contact.adapt.

thanks,
Isaac

P.S. I have an updated version which generates methods instead of
lookup + some extras. Nobody mind if I post it to the ml later(ie no
negative)?

路路路

On Wed, 28 Dec 2005 23:24:01 +0900 Florian Gro脽 <florgro@gmail.com> wrote:

> I have been hacking away to create a simple library that adds
> generics-like qualities to ruby. At the moment you can define
> methods with type-matches, defaults if no match and different
> number of argument matches.

Been there, done that:

http://ruby-contract.rubyforge.org/

New ideas are very welcome.

I have been hacking away to create a simple library that adds
generics-like qualities to ruby. At the moment you can define
methods with type-matches, defaults if no match and different
number of argument matches.

Been there, done that:

http://ruby-contract.rubyforge.org/

New ideas are very welcome.

Thanks! I've quickly looked at the ruby-doc for that. It's seems to
only be able to specific "contracts" for classes, with method
signatures as a subset. What my goal for generics is to be able to
choose what code to execute based on method/class parameter types.
ie.
converting :
def foo(arg)
if arg.kind_of? String
puts "it is a string! reverse it! #{arg.reverse}"
elsif arg.kind_of? Fixnum
puts "Fixnum! double it! #{2*arg}"
end
end

into:

generic_method :foo, String do |arg|
puts "it is a string! reverse it! #{arg.reverse}"
end

generic_method :foo, Fixnum do |arg|
puts "Fixnum! double it! #{2*arg}"
end

That's not generics but overloading. I'm sorry, but with these things it's really important to use teminology properly otherwise you'll likely cause a lot of misunderstandings.

In the future I would like to be able to extend this to classes as
well. One implication of this we will be the ability to get rid of
"adapter" classes when joining heirachys. One example would be adding
support to reading from a String when a class can only read from a
File.

ie.
generic_class :SomeClass, String do
def getline
..
end

IMHO this is a bad example because you can turn a String into a StringIO which supports IO like behavior. If some method works on an IO instance it almost always works on a StringIO, too.

Another wish is for pattern matching:
  generic_method :foo, :a, :b :a do ...
where wherever :a occurs it must be the same type so:
  foo 4 "a" 4 passes
but
  foo "a" 4 4 fails.

Sounds like you wanted to reimplement some features very common with functional languages in Ruby. Why do you do that?

P.S. I have an updated version which generates methods instead of
lookup + some extras. Nobody mind if I post it to the ml later(ie no
negative)?

If you really intend to go further down that road I suggest you create a project on rubyforge (or merge with an existing project).

I'm sorry to be so discouraging but this seems like yet another attempt to retrofit other languages' features to Ruby instead of using it the way it is.

Kind regards

    robert

路路路

Isaac Devine <isaac.devine@gmail.com> wrote:

On Wed, 28 Dec 2005 23:24:01 +0900 > Florian Gro脽 <florgro@gmail.com> wrote:

Hi --

I have been hacking away to create a simple library that adds
generics-like qualities to ruby. At the moment you can define
methods with type-matches, defaults if no match and different
number of argument matches.

Been there, done that:

http://ruby-contract.rubyforge.org/

New ideas are very welcome.

Thanks! I've quickly looked at the ruby-doc for that. It's seems to
only be able to specific "contracts" for classes, with method
signatures as a subset. What my goal for generics is to be able to
choose what code to execute based on method/class parameter types.

Keep in mind that type != class in Ruby. I think what you're
describing is class/module ancestry (the kind of thing you can test
with kind_of?) rather than type.

Type is, in a sense, circular: an object's type is essentially "the
type which is the type of objects which do what this object does".
Among other things, that means that for practical purposes objects can
be of multiple types.

The usual way for objects to identify themselves as suitable for a
given purpose is by their type -- that is, by the criterion of what
messages they understand. Class membership can give you a likely
answer to this, but not a definite one. Depending on kind_of? can
also limit your ability to make objects of different classes all
converge on a particular capability. It tends to discourage thinking
in terms of type and object-centered programming possibilities,
including duck typing.

Anyway -- you can of course use kind_of? all you like :slight_smile: But keep in
mind that it's not a type-test.

David

路路路

On Thu, 29 Dec 2005, Isaac Devine wrote:

On Wed, 28 Dec 2005 23:24:01 +0900 > Florian Gro脽 <florgro@gmail.com> wrote:

--
David A. Black
dblack@wobblini.net

"Ruby for Rails", from Manning Publications, coming April 2006!

Isaac Devine wrote:

Thanks! I've quickly looked at the ruby-doc for that. It's seems to
only be able to specific "contracts" for classes, with method
signatures as a subset.

Hm, actually it provides a way of using unit tests for checking types, but will still let you do strong typing (via classes) and duck typing (via messages).

It then defines a way of adding type annotations to methods. Where types still means contract/class/message etc.

[...] choose what code to execute based on method/class parameter types.
[...] support to reading from a String when a class can only read from a File.
[...] Another wish is for pattern matching: [...]

Looking at the rdoc some code in that could be very helpful - such as
Contact.adapt.

Basically at the moment I do type annotations for methods. So you could for example specify that you have a method that needs a Symbol and the Contract library would make sure that an exception will be raised for Fixnums -- it would however automatically invoke .to_sym (because there is an adaption route to Symbol for that) so Strings would work as well.

The problem with this is that all these conversions are explicit and that you can't define custom conversions that will just apply in the scope of your methods or classes. It's hard to find a good balance this way, because too much automatic conversion can very easily cause very surprising results.

I've not yet coded up multi method dispatch (which seems to be your primary goal for now), but it would be possible. For now you would still need to do the dispatching yourself by using ruby-contract's methods for checking whether your arguments match given types and then doing whatever you want to happen.

Pattern matching is interesting as well, but I can't think of a good implementation right now.

If you can come up with ways of integrating the functionality that you want in a clean way into ruby-contract then I'd be pleased to merge it. But even if it is hard to integrate this into ruby-contract's API feel free to build on the functionality it already has.

I tried hard to have unit tests with high coverage so reading through them might give you a bit more feeling of what is already there and how it can be used.

路路路

--
http://flgr.0x42.net/

Hi --

路路路

On Thu, 29 Dec 2005, Florian Gro脽 wrote:

Isaac Devine wrote:

Thanks! I've quickly looked at the ruby-doc for that. It's seems to
only be able to specific "contracts" for classes, with method
signatures as a subset.

Hm, actually it provides a way of using unit tests for checking types, but will still let you do strong typing (via classes) and duck typing (via messages).

How about strong typing via messages? :slight_smile:

David

--
David A. Black
dblack@wobblini.net

"Ruby for Rails", from Manning Publications, coming April 2006!

Maybe this is because these features are found to be useful.

路路路

On 12/29/05, Robert Klemme <bob.news@gmx.net> wrote:

Isaac Devine <isaac.devine@gmail.com> wrote:
> Another wish is for pattern matching:
> generic_method :foo, :a, :b :a do ...
> where wherever :a occurs it must be the same type so:
> foo 4 "a" 4 passes
> but
> foo "a" 4 4 fails.

Sounds like you wanted to reimplement some features very common with
functional languages in Ruby. Why do you do that?

dblack@wobblini.net wrote:

Hm, actually it provides a way of using unit tests for checking types, but will still let you do strong typing (via classes) and duck typing (via messages).

How about strong typing via messages? :slight_smile:

I guess I should have said static typing. Though that is not exclusive too classes either. I guess the only right term is "class based typing" then?

路路路

--
http://flgr.0x42.net/

Florian Gro脽 wrote:

>> Hm, actually it provides a way of using unit tests for checking types,
>> but will still let you do strong typing (via classes) and duck typing
>> (via messages).
> How about strong typing via messages? :slight_smile:

I guess I should have said static typing. Though that is not exclusive
too classes either. I guess the only right term is "class based typing"
then?

Static typing means the type checking happens at "compile" time (or NOT
during the execution of the program!). You mean Nominal typing
(nominal as in name (using in this case classname)) as opposed to
structural (messages / aka duck) typing. Strong typing means that you
can't get undefined behaviour out of the system at runtime (all 'type
errors' are caught, generally in this case by method missing errors
getting thrown).

As another poster said, this terminology needs to be used correctly
otherwise people do get confused :slight_smile:

Tris

路路路

dblack@wobblini.net wrote:

ToRA wrote:

Florian Gro脽 wrote:
>
> >> Hm, actually it provides a way of using unit tests for checking types,
> >> but will still let you do strong typing (via classes) and duck typing
> >> (via messages).
> > How about strong typing via messages? :slight_smile:
>
> I guess I should have said static typing. Though that is not exclusive
> too classes either. I guess the only right term is "class based typing"
> then?
Static typing means the type checking happens at "compile" time (or NOT
during the execution of the program!). You mean Nominal typing
(nominal as in name (using in this case classname)) as opposed to
structural (messages / aka duck) typing. Strong typing means that you
can't get undefined behaviour out of the system at runtime (all 'type
errors' are caught, generally in this case by method missing errors
getting thrown).

As another poster said, this terminology needs to be used correctly
otherwise people do get confused :slight_smile:

"Nominal/structural" typing, haven't seen that one before. Artima
seems to have purged all their dogfights about what static/dynamic
strong/weak typing means, but you can read google caches, e.g.

http://66.102.7.104/search?q=cache:9zZxPZdlqR4J:www.artima.com/forums/flat.jsp%3Fforum%3D32%26thread%3D3572+site:artima.com+strong+typing&hl=en&client=firefox-a

Seems to me that ruby developers can agree on what strong / dynamic
typing means, but when you go out and encounter people from smalltalk,
lisp, haskell, ML, java/C#, ... Also all those *nice* discussions
about what overloading/polymorphism means, delegation/composition,
pass by value/reference ;-{}

路路路

> dblack@wobblini.net wrote:
Tris

ToRA wrote:

Static typing means the type checking happens at "compile" time (or NOT
during the execution of the program!). You mean Nominal typing
(nominal as in name (using in this case classname)) as opposed to
structural (messages / aka duck) typing. Strong typing means that you
can't get undefined behaviour out of the system at runtime (all 'type
errors' are caught, generally in this case by method missing errors
getting thrown).

Already knew about dynamic <> static and strong <> weak.

But nominal <> structural is what I was looking for in this case. Thanks!

路路路

--
http://flgr.0x42.net/

Florian Gro脽 wrote:

ToRA wrote:

Static typing means the type checking happens at "compile" time (or NOT
during the execution of the program!). You mean Nominal typing
(nominal as in name (using in this case classname)) as opposed to
structural (messages / aka duck) typing. Strong typing means that you
can't get undefined behaviour out of the system at runtime (all 'type
errors' are caught, generally in this case by method missing errors
getting thrown).

Already knew about dynamic <> static and strong <> weak.

But nominal <> structural is what I was looking for in this case. Thanks!

I have never heard these terms used this way, but it is interesting.

Are those in common use by some group of people somewhere?

Hal

Hal Fulton wrote:

Already knew about dynamic <> static and strong <> weak.

But nominal <> structural is what I was looking for in this case. Thanks!

I have never heard these terms used this way, but it is interesting.

Are those in common use by some group of people somewhere?

At least the first two pairs ought to be unless I misremembered them.

路路路

--
http://flgr.0x42.net/

Hal Fulton ha scritto:

Already knew about dynamic <> static and strong <> weak.

But nominal <> structural is what I was looking for in this case. Thanks!

I have never heard these terms used this way, but it is interesting.

Are those in common use by some group of people somewhere?

all the people that actually understand typing issues, it seem :slight_smile:
And with this I mean: lambda-the-ultimate.org and the people working with languages such as ML dialects, haskell and in general functional stuff with static checks. At least it seem to me.

Florian Gro脽 wrote:

Hal Fulton wrote:

Already knew about dynamic <> static and strong <> weak.

But nominal <> structural is what I was looking for in this case. Thanks!

I have never heard these terms used this way, but it is interesting.

Are those in common use by some group of people somewhere?

At least the first two pairs ought to be unless I misremembered them.

Sure, I know those. It was the nominal/structural I was
asking about.

Hal

Hey,

Well I'm a uni student (with a loose interest in type-theory) and its
cropped up in some literature I'm reading. Usually under the guise of
structural sub-typing and nominal sub-typing; I recall some discussion
on Lambda-the-ultimate on whether structural subtyping == duck typing.
(http://lambda-the-ultimate.org/node/view/1201)

Also see the wikipedia article on subtype
(http://en.wikipedia.org/wiki/Subtype), certainly in published papers
in the typing area these terms come up.

HTH,

Regards,

Tris.