[ANN] RubyTraits 0.1

R

Robert Dober

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.
If what you did solves your problem than that is good, it however does
not address the issues that are adressed by traits.
And the good thing about traits is that, if you do not want to use
them, then just do not.
I have followed the discussion on the Squeak Mailing List before
making traits part of the kernel. The killer argument was, ok we are
going to do this because it will make the code base "better" but
nobody will ever need to care about Traits unless they want.
To be fair, Squeak needed Traits much more than Ruby as they had no Mixins.
OTOH I have never heard anybody complain that traits remind them of
static typing, would you mind to elaborate on that Daniel?

Cheers
Robert
 
D

Daniel Berger

Robert said:
If what you did solves your problem than that is good, it however does
not address the issues that are adressed by traits.

The multi-mixin (i.e. total ordering) problem is one of the primary
reasons for Traits, or at least, one of the main digs against mixin
inheritence. My library solves that in a dynamic, but not strict, manner.
And the good thing about traits is that, if you do not want to use
them, then just do not.

As a third party library they're fine. But core? No.
I have followed the discussion on the Squeak Mailing List before
making traits part of the kernel. The killer argument was, ok we are
going to do this because it will make the code base "better" but
nobody will ever need to care about Traits unless they want.
To be fair, Squeak needed Traits much more than Ruby as they had no Mixins.

Exactly. In languages that have no mixins I think Traits are fine and,
from what I can gather, it's the mixin capability those languages and
communities are really after, not the composition.
OTOH I have never heard anybody complain that traits remind them of
static typing, would you mind to elaborate on that Daniel?

In a couple of ways. First, raising an error if method conflicts arise
feels too strict for a dynamic language. I'd like a warning, but nothing
more. Also, a trait requires a set of methods that serve as parameters
for the provided behavior. Like static typing, that means up-front
declarations that don't mesh well with the overall philosophy of dynamic
languages IMO.

Regards,

Dan
 
R

Robert Dober

The multi-mixin (i.e. total ordering) problem is one of the primary
reasons for Traits, or at least, one of the main digs against mixin
inheritence. My library solves that in a dynamic, but not strict, manner.


As a third party library they're fine. But core? No.
I was not thinking of making it core, and Matz said too that having
Mixins already he will refrain from putting them into core, I feel
that this is a reasonable choice, no hard feelings at all.
In a couple of ways. First, raising an error if method conflicts arise
feels too strict for a dynamic language. I'd like a warning, but nothing
more. Also, a trait requires a set of methods that serve as parameters
for the provided behavior. Like static typing, that means up-front
declarations that don't mesh well with the overall philosophy of dynamic
languages IMO.
Hmm honestly I think that your conclusion is correct but your premises are not.
Traits are as dynamical as modules if you just "use" them, the conflict handling
is an extra you get from combining them.
In the semantics of my traits package that would be
class A
use t1, t2 # conflicts might be defined as this is equivalent to
# use t1+t2 which is equivalent to use t2 + t1 per definition
end

class A
use t1
use t2
end

does almost exactly what you would have with modules only that use t2
will always take effect because of the flattening property.

Forgive me if I am too religious about this, but I feel that traits
are an exciting thing :).

But there is one thing which is "wrong" with my package if you are
going to dynamically extend traits they will break, but that is the
fault of my - simple - implementation not really the fault of traits
:(, it might even be fixable.

Thx for your time.

Cheers
Robert
 
T

Trans

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 couldn't help but think about what a solution to that might look
like --it would have to have the layerability of modules but the
simplicity of traits. To do that, I think methods would have to be
more independent. Instead of a Method being a single function
belonging to class (or module/trait) it would be a linked-list of
them. Each of these would have a second link tying it to the trait/
module. Eg.

.-> Class1
| .-> TraitX .-> TraitY
.-------- | |
| -> foo_method-> foo_method -> foo_method
'--------

It would also be possible to do around advice very easily too, because
a method could be added to the chain that's not tied to a Triat/
Module.

.-> Class1
| .-> TraitX .-> TraitY
.-------- | |
| -> foo_method -> foo_method -> foo_method -> foo_method
'--------

#super would be dynamically resolved by following the link.

I think this could provide the advantages of both traits and modules
in a single system.

T.
 
T

Trans

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

I have to agree that traits are too much alike and yet too different
from modules to co-exist in the same language. However, I disagree
with this subsequent statement. Much of the benefit of traits can
still be had by overlaying the same concepts on Ruby's modules. In the
end I think the most important part is usability, not so much the
underlying implementation (as long as it is reasonably efficient, of
course). Moreover, I contend that if you are serious when you say, you
would have used traits instead of modules if he had known about them,
then you were fortunate not to have known, b/c modules are a more
powerful composition mechanism, despite their greater implementation
complexity.

To demonstrate my point here is Facets' module/traits.rb lib. I
improved it a fair bit yesterday --thanks to Robert and this thread.
I'd like others to have a look and provide any feedback. As you will
see in the code, I still have a couple questions about where to use
public vs. all instance methods (which leads me to question/thought
that I will bring up in another thread). Here you go...

class Module

# Combine modules.

def +(other)
base = self
Module.new do
include base
include other
end
end

# Subtract modules.
#--
# TODO Should this use all instance_methods, not just public?
#++
def -(other)
case other
when Array
subtract = instance_methods(true) & other.collect{|m| m.to_s}
when Module
subtract = instance_methods(true) &
other.instance_methods(true) # false?
when String, Symbol
subtract = instance_methods(true) & [other.to_s]
end
base = self
Module.new do
include base
subtract.each{ |x| undef_method x }
end
end

# Rename methods.

def *(rename_map)
base = self
Module.new do
include base
rename_map.each do |from, to|
alias_method to, from
undef_method from
end
end
end

# Detect conflicts.
#--
# TODO All instance methods, or just public?
#++
def conflict?(other)
c = []
c += (public_instance_methods(true) &
other.public_instance_methods(true))
c += (private_instance_methods(true) &
other.private_instance_methods(true))
c += (protected_instance_methods(true) &
other.protected_instance_methods(true))
c.empty ? false : c
end

#def conflict?(other)
# c = instance_methods & other.instance_methods
# c.empty ? false : c
#end

def public_conflict?(other)
c = public_instance_methods(true) &
other.public_instance_methods(true)
c.empty ? false : c
end

def private_conflict?(other)
c = private_instance_methods(true) &
other.private_instance_methods(true)
c.empty ? false : c
end

def protected_conflict?(other)
c = protected_instance_methods(true) &
other.protected_instance_methods(true)
c.empty ? false : c
end

end


Thanks,
T.
 

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,780
Messages
2,569,608
Members
45,250
Latest member
Charlesreero

Latest Threads

Top