[ANN] RubyTraits 0.1

R

Robert Dober

Hi list

I have just released ruby-traits v0.1. This are Traits in pure Ruby 1.8.
Have fun.

----------------------------------------------------------------------------------------
Traits for Ruby
==========

Traits are Composable Units of Behavior, well that is the
academic title,

For Rubiests the following would be a good definition:

Mixins with Conflict Resolution and a Flattening Property
allowing to avoid subtle problems like Double Inclusion and
calling method x from Mixin L while we wanted method x from
Mixin N.
There is some (extra/nice?) composition syntax too

For details please refer to:
http://portal.acm.org/ft_gateway.cf...=GUIDE&dl=GUIDE&CFID=7912496&CFTOKEN=77115102
which is a PhD thesis defining traits formally.

And yes Traits are implemented in Squeak 3.9.

In practice Traits enable us to:
* get a RuntimeError when we call a method defined by
more than one trait.
These conflicts can be resolved by redefining the method.
* avoid any double inclusion problem.
* compose Traits
* alias methods during Trait Composition
* resolve super dynamically (mentioned for completeness, Ruby modules
can do this too, of course ;)

Examples:
t1 = trait { def a; 40 end }
t2 = Trait::new{ def a; 2 end }
c1 = Class::new {
use t1, t2
}
c1.new.a --> raises TraitsConflict

conflicts can be resolved be redefinition, and aliasing can be used for
access to the overriden methods. All this can be combined
with traits composition.
t = ( t1 + { :a => :t1_a } ) + ( t2 + {:a => :t2_a } )
c2 = Class::new {
use t
def a; t1_a + t2_a end
}
c2.new.a --> 42
 
R

Robert Dober

Hi,

In message "Re: [ANN] RubyTraits 0.1"

|Traits for Ruby
|==========
|
| Traits are Composable Units of Behavior, well that is the
| academic title,

I like this. Thank you.

matz.
Great to hear, thx.
I have heared rumors that traits will be in Ruby2 some day, that would be great!
Robert
 
T

Trans

Hi list

I have just released ruby-traits v0.1. This are Traits in pure Ruby 1.8.
Have fun.

-------------------------------------------------------------------------= --=AD-------------
Traits for Ruby
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D

Traits are Composable Units of Behavior, well that is the
academic title,

For Rubiests the following would be a good definition:

Mixins with Conflict Resolution and a Flattening Property
allowing to avoid subtle problems like Double Inclusion and
calling method x from Mixin L while we wanted method x from
Mixin N.
There is some (extra/nice?) composition syntax too

For details please refer to:
http://portal.acm.org/ft_gateway.cfm?id=3D1028771&type=3Dpdf&coll=3DGU= IDE&d...
which is a PhD thesis defining traits formally.

And yes Traits are implemented in Squeak 3.9.

In practice Traits enable us to:
* get a RuntimeError when we call a method defined by
more than one trait.
These conflicts can be resolved by redefining the method.
* avoid any double inclusion problem.
* compose Traits
* alias methods during Trait Composition
* resolve super dynamically (mentioned for completeness, Ruby modules
can do this too, of course ;)

Examples:
t1 =3D trait { def a; 40 end }
t2 =3D Trait::new{ def a; 2 end }
c1 =3D Class::new {
use t1, t2
}
c1.new.a --> raises TraitsConflict

conflicts can be resolved be redefinition, and aliasing can be used for
access to the overriden methods. All this can be combined
with traits composition.
t =3D ( t1 + { :a =3D> :t1_a } ) + ( t2 + {:a =3D> :t2_a } )
c2 =3D Class::new {
use t
def a; t1_a + t2_a end
}
c2.new.a --> 42

This is very similar to traits.rb in Facets. But Facets does this with
regular modules, not special Traits class.

So how are you composing these traits? Is it delegation or code
injection?

T=2E
 
T

Trans

Hi,

In message "Re: [ANN] RubyTraits 0.1"

|I have heared rumors that traits will be in Ruby2 some day, that would be great!

Even though I like the concept of traits, traits and modules are too
close in concept, too different in behavior. Having both in a
language may cause confusion. Too bad I didn't know about traits when
I designed Ruby.

What behavior difference are you thinking about? To the end-programmer
wouldn't it look mostly the same?

T.
 
T

Trans

Hi,

In message "Re: RubyTraits 0.1"

|> Even though I like the concept of traits, traits and modules are too
|> close in concept, too different in behavior. Having both in a
|> language may cause confusion. Too bad I didn't know about traits when
|> I designed Ruby.
|
|What behavior difference are you thinking about? To the end-programmer
|wouldn't it look mostly the same?

Yes, that's why I said "too close" to have them both.

The biggest difference I concern is traits inject attributes where
modules create relationship. Modules can conflict (overriding rule
applied), traits can't.

If I knew traits before designing Ruby, I'd have chosen traits over
modules. But that's the life.

I see. So you don't really like the fact that modules fit into the
inheritance chain? That's interesting. The traits lib I wrote for
Facets actually uses the inheritance. Eg:

class Module

def +( other )
mod1 = other.clone
mod2 = clone
mod1.module_eval{ include mod2 }
return mod1
end

So instead of injection there's relationship, but in every other
respect its like traits. Is that not a good Ruby-esque way to do
traits? Maybe even better than injection actually?

T.
 
R

Robert Dober

Hi,

In message "Re: RubyTraits 0.1"

|I see. So you don't really like the fact that modules fit into the
|inheritance chain? That's interesting.

Actually they both have their own good. I like them both. I dislike
to have them both in a language. Traits injection is clear and less
error prone (there's no possibility of conflicts), but sometimes
method overriding is _very_ useful, where aliasing is very poor way to
create method combination.
Actually Traits allow both
when you compose traits conflicts will be created
when you use traits (that is when you inject the methods) they can
perfectly be overridden.

Example
t1 = trait { def a; 42 end }
t2 = trait { def a; 46 end }
t3 = t1 + t2
class A
use t3
end
A.new.a --> Conflict
but
class A
use t1 # or t3 for that matter ### flattening is going on here
def a; 22 end
end
A.new.a --> 22
according to Schaerli's paper you can even do
t4 = trait { use t1; def a; 222 end }
no conflicts here

The flattening property of traits will however hide the trait from the
inheritance chain, behavior is composed not inherited that is an
important trait of traits - forgive the pun ;).
If someone come to the idea to allow modules to have merits from both
mixins and traits at once (without demerit), I'd love to hear.
Hmm, I almost fail to see what merit modules have that traits do not have?
That you can say isa? for an instance of a class that had a module mixed in?
Maybe, no idea if this is worth it.

Cheers
Robert
 
D

Daniel Berger

Hi,

In message "Re: RubyTraits 0.1"

|I see. So you don't really like the fact that modules fit into the
|inheritance chain? That's interesting.

Actually they both have their own good. I like them both. I dislike
to have them both in a language. Traits injection is clear and less
error prone (there's no possibility of conflicts), but sometimes
method overriding is _very_ useful, where aliasing is very poor way to
create method combination.

If someone come to the idea to allow modules to have merits from both
mixins and traits at once (without demerit), I'd love to hear.

I solved this "problem" over two years ago with fine grained mixins.
Please see http://rubyforge.org/docman/view.php/735/309/README.html
and look at the synopsis. Ideas (and code) in that library also came
from Ara Howard and Mauricio Fernandez.

I don't want up front object composition. To me that's like static
typing, except for object composition. At worst, a warning should be
issued if a double inclusion occurs. This is what the 'use' library
does in verbose mode.

Regards,

Dan
 
D

Daniel Berger

Hi,

In message "Re: RubyTraits 0.1"

|> If someone come to the idea to allow modules to have merits from both
|> mixins and traits at once (without demerit), I'd love to hear.
|
|I solved this "problem" over two years ago with fine grained mixins.
|Please seehttp://rubyforge.org/docman/view.php/735/309/README.html
|and look at the synopsis. Ideas (and code) in that library also came
|from Ara Howard and Mauricio Fernandez.

Pardon my ignorance. I will check the code.

No pardon required. :)

BTW, I uploaded some more examples here:

http://rubyforge.org/docman/view.php/735/2472/examples.html

Regards,

Dan
 
T

Trans

This is an interesting discussion --what qualifies as "traits".

Actually Traits allow both
when you compose traits conflicts will be created
when you use traits (that is when you inject the methods) they can
perfectly be overridden.

Example
t1 = trait { def a; 42 end }
t2 = trait { def a; 46 end }
t3 = t1 + t2
class A
use t3
end
A.new.a --> Conflict
but
class A
use t1 # or t3 for that matter ### flattening is going on here
def a; 22 end
end
A.new.a --> 22
according to Schaerli's paper you can even do
t4 = trait { use t1; def a; 222 end }
no conflicts here

Interesting. But why not have a default definition based on
combination order instead of raising a Conflict? One can always
override/redefine. But having a conflict forces one to redefine, which
seems more limiting to me. But maybe I missing something, I haven't
read the paper (link?).
The flattening property of traits will however hide the trait from the
inheritance chain, behavior is composed not inherited that is an
important trait of traits - forgive the pun ;).

Is it an absolutely necessary trait, though? The theory defines
flattening b/c it is offering an alternate means of composition.
However, we have module chains at our disposal. Can we not take
advantage of them in our implementation and still be considered
"traits"? Might we not view Ruby's linearization of the inheritance
chain as an effective means of "flattening", whether it is or not in
the strictest sense? Or are there some other important reasons for
flattening (especially one that Ruby's module chains can't emulate)?
Hmm, I almost fail to see what merit modules have that traits do not have?
That you can say isa? for an instance of a class that had a module mixed in?
Maybe, no idea if this is worth it.

That's one reason. But more importantly, calling super, ie. method
inheritance. Does the formal design of traits have super?

T.
 
D

Daniel Berger

Hi,

In message "Re: RubyTraits 0.1"

|BTW, I uploaded some more examples here:
|
|http://rubyforge.org/docman/view.php/735/2472/examples.html

How do you think one can choose use and include?

I chose the word "use" because I thought it might be inappropriate to
redefine "include", not because I thought it needed a separate name in
order to distinguish behavior. Note that "use ModA" with no arguments
is the same as "include ModA".
Should :alias be named :rename if it doesn't keep old name?

Hm, you're probably right about that. :)

Regards,

Dan
 
P

Phrogz

I chose the word "use" because I thought it might be inappropriate to
redefine "include", not because I thought it needed a separate name in
order to distinguish behavior. Note that "use ModA" with no arguments
is the same as "include ModA".

Although this syntax reads well:
include ModA, :exclude => :foo
I think this particular syntax appears odd:
include ModA, :include => :foo
where this is better:
include ModA, :include => :foo

If 'include' were to be preserved in place of 'use', perhaps instead:
include ModA, :exclude => :foo
include ModB, :eek:nly => :bar #instead of a 2nd 'include'
 
T

Trans

I solved this "problem" over two years ago with fine grained mixins.
Please seehttp://rubyforge.org/docman/view.php/735/309/README.html
and look at the synopsis. Ideas (and code) in that library also came
from Ara Howard and Mauricio Fernandez.

I don't want up front object composition. To me that's like static
typing, except for object composition. At worst, a warning should be
issued if a double inclusion occurs. This is what the 'use' library
does in verbose mode.

Kind of like #integrate.

# Using integrate is just like using include except the
# module included is a reconstruction of the one given
# altered by the commands given in the block.
#
# Convenient commands available are: #rename, #redef,
# #remove, #nodef and #wrap. But any module method
# can be used.
#
# module W
# def q ; "q" ; end
# def y ; "y" ; end
# end
#
# class X
# integrate W do
# nodef :y
# end
# end
#
# x = X.new
# x.q #=> "q"
# x.y #=> NoMethodError
#
# This is like #revisal, but #revisal only
# returns the reconstructred module. It does not
# include it.

The significant difference between this and your #use method is that
#use can more easily add a small select group of methods from a module
--I think that's that gist of your lib. However, if you find yourself
"cherry picking" methods from a module like that, it's time to rethink
the design. Plus, implementation of that is terribly inefficient
(having to remove all other methods).

At any rate, Traits are much prettier:

class X
include W - :y
end

T.
 
P

Phrogz

I'm staring and staring and staring, but #2 still looks the same as #3.

If I cross my eyes, they still look the same, but I also see a 3D Ferris
wheel pop out.

Uhm. Yeah. Oops. :)

I had meant to type:
Although this syntax reads well:
include ModA, :exclude => :foo
I think this particular syntax appears odd:
include ModA, :include => :foo
where this is better:
use ModA, :include => :foo
....but apparently got lazy. Sorry 'bout that.

Have fun at the 3D circus. :)
 
D

Daniel Berger

BTW, I uploaded some more examples here:

I think you have a minor typo:

class Bar
use Mod_B, :include => [:meth_b, :meth_c] # include only 'meth_a'
and 'meth_b'
use Mod_B, :exclude => :meth_c # same net result as
above
end

Shouldn't that be :exclude => :meth_a instead, to get the same net
result?

It should be ":include => [:meth_a, :meth_b]"

Fixed at http://rubyforge.org/docman/view.php/735/2472/examples.html.

Thanks,

Dan
 
D

Daniel Berger

Uhm. Yeah. Oops. :)

I had meant to type:> Although this syntax reads well:

...but apparently got lazy. Sorry 'bout that.

The :include option is the default if no keywords are supplied. So,
you can do this:

use ModA, :foo

If we override 'include', then it's just this:

include ModA, :foo

Regards,

Dan
 
D

Daniel Berger

The significant difference between this and your #use method is that
#use can more easily add a small select group of methods from a module
--I think that's that gist of your lib. However, if you find yourself
"cherry picking" methods from a module like that, it's time to rethink
the design. Plus, implementation of that is terribly inefficient
(having to remove all other methods).

At any rate, Traits are much prettier:

class X
include W - :y
end

I think excluding specific methods would be unusual in practice. Of
course, I'm of the opinion that traits are a solution in search of a
problem to begin with, so take my opinions with a grain of salt.

Anyway, I suppose I could add an option where it simply discards dup's
dynamically when a module is included:

class X
use ModA, :no_override
end

That would be interpreted as "include all methods unless already
defined".

Regards,

Dan
 
R

Robert Dober

On 10/15/07 said:
That's one reason. But more importantly, calling super, ie. method
inheritance. Does the formal design of traits have super?
Yup it is one of the cornerstones of the definition, super is resolved
dynamically
in the inheritance chain.
R
 

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

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top