RubyTraits 0.1

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.cfm?id=1028771&type=pdf&coll=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 :wink:

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

Hi,

In message “Re: [ANN] RubyTraits 0.1”
on Mon, 15 Oct 2007 18:40:04 +0900, “Robert D.”
[email protected] writes:

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

I like this. Thank you.

          matz.

On 10/15/07, Yukihiro M. [email protected] wrote:

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

On Oct 15, 5:40 am, “Robert D.” [email protected] wrote:

academic title,
http://portal.acm.org/ft_gateway.cfm?id=1028771&type=pdf&coll=GUIDE&d

  • alias methods during Trait Composition

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

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.

On 10/15/07, Trans [email protected] wrote:

==========
There is some (extra/nice?) composition syntax too
These conflicts can be resolved by redefining the method.
use t1, t2
}
c2.new.a → 42

This is very similar to traits.rb in Facets. But Facets does this with
regular modules, not special Traits class.
Oh gosh I reinvented the wheel.

So how are you composing these traits? Is it delegation or code
injection?
Each Trait has a flattened list of modules and a hash mapping all method
names
to a pair of module and trait (the later used to avoid false conflicts).
I wanted to do code injection first thus in my initial version
t = trait { def a *args, &blk; … }
class C
use t
end
the use t
just did a define_method :a, t.methods[:a]
this worked beautifully until I tested super which broke miserably, I
also realised that in order to be able to do the define_method I had
to include the module in the class
and I got a messy inheritance chain, so I redesigned it

Now when I do use t
the class just includes all modules but in order to have no double
inclusion problem
and to achieve the flattening property I have to include copies of the
modules.
Maybe it was just me but it seems Module#dup does not work reliabely,
I therefore
created TraitModule < Module which allowed me to kill two birds with a
stone.
I defined #dup by keeping the block in a TraitModule ivar and I fixed
a bug in my aliasing code.

Well that might be beyond the scope of the list, if you want to
continue discussion feel free to mail me OL.

Gosh should have checked Facets, really.

Cheers
Robert

On Oct 15, 6:08 am, Yukihiro M. [email protected] wrote:

I designed Ruby.
What behavior difference are you thinking about? To the end-programmer
wouldn’t it look mostly the same?

T.

Hi,

In message “Re: [ANN] RubyTraits 0.1”
on Mon, 15 Oct 2007 19:01:10 +0900, “Robert D.”
[email protected] writes:

|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.

          matz.

Hi,

In message “Re: RubyTraits 0.1”
on Mon, 15 Oct 2007 23:39:24 +0900, Trans [email protected]
writes:

|> 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.

          matz.

On Oct 15, 8:11 am, Yukihiro M. [email protected] wrote:

|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.

On 10/15/07, Yukihiro M. [email protected] wrote:

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

Hi,

In message “Re: RubyTraits 0.1”
on Tue, 16 Oct 2007 00:23:43 +0900, Trans [email protected]
writes:

|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.

          matz.

On Oct 15, 9:38 am, Yukihiro M. [email protected] wrote:

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 F…

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

On Oct 15, 10:55 am, Yukihiro M. [email protected] wrote:

|and look at the synopsis. Ideas (and code) in that library also came
|from Ara Howard and Mauricio F…

Pardon my ignorance. I will check the code.

No pardon required. :slight_smile:

BTW, I uploaded some more examples here:

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

Regards,

Dan

Hi,

In message “Re: RubyTraits 0.1”
on Tue, 16 Oct 2007 01:49:05 +0900, Daniel B.
[email protected] writes:

|> 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 F…

Pardon my ignorance. I will check the code.

          matz.

This is an interesting discussion --what qualifies as “traits”.

On Oct 15, 8:50 am, “Robert D.” [email protected] wrote:

use t3
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)?

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.

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

T.

Hi,

In message “Re: RubyTraits 0.1”
on Tue, 16 Oct 2007 02:23:30 +0900, Daniel B.
[email protected] writes:

|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?
Should :alias be named :rename if it doesn’t keep old name?

          matz.

On Oct 15, 11:23 am, Daniel B. [email protected] wrote:

BTW, I uploaded some more examples here:

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

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?

On Oct 15, 11:29 am, Yukihiro M. [email protected] wrote:

Hi,

In message “Re: RubyTraits 0.1”
on Tue, 16 Oct 2007 02:23:30 +0900, Daniel B. [email protected] writes:

|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. :slight_smile:

Regards,

Dan

On Oct 15, 12:54 pm, Daniel B. [email protected] wrote:

On Oct 15, 11:29 am, Yukihiro M. [email protected] wrote:

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”.

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, :only => :bar #instead of a 2nd ‘include’

On Oct 15, 9:49 am, Daniel B. [email protected] wrote:

|inheritance chain? That’s interesting.
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 F…

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.