[ANN] Ruby-Traits 0.2 released (sorry long)

Discussion in 'Ruby' started by Robert Dober, Oct 23, 2007.

  1. Robert Dober

    Robert Dober Guest

    Hi list

    after some very interesting input I decided to rewrite Ruby-Traits completely.

    http://rubyforge.org/frs/?group_id=4642&release_id=15737

    I have decided to implement two different kinds of traits:
    * ruby-traits
    and
    * pure-traits.

    In detail:
    * ruby-traits are mimicking some of the properties of traits but violate the
    flattening property. They implement some of the more dynamic features
    of modules and are in fact module based. The code is heavily influenced
    by Thomas Sawyer's traits from Facets. Many thanks to Thomas for the
    permission to use his code. Note that Thomas' traits of Facets is yet another
    different beast.

    * pure-traits implement traits in the strict sense of their definition
    here: http://portal.acm.org/ft_gateway.cf...=GUIDE&dl=GUIDE&CFID=7912496&CFTOKEN=77115102

    They obey the flattening property, using a trait is exactly as defining
    its methods inline, no modules are used and inheritance is unchanged.
    All trait combination rules are obeyed (associative, communative, e.g.)

    The main purpose of pure-traits is to play around with them and to see the
    benefits and shortcomings of traits based design.

    The syntax has changed, again inspired by Factes, trait aliasing is done
    via #* now, thus traits aliased and combined can be expressed more
    naturally:
    t1 * { :t1_a => :a } + t2 * { :t2_a => :a }
    works now as expected.

    In addition to the traditional trait operations +, - and *, I have
    implemented & to allow of selective usage of methods.
    I believe that the usage of this is normally a bad sign for the design
    but sometimes it might be a handy feature.

    -------------------------------------------------------------------------------------
    Pure Traits
    -------------------------------------------------------------------------------------
    # Traits are Composable Units of Behavior. they are described here in detail
    # The interested can look here
    # http://portal.acm.org/ft_gateway.cf...=GUIDE&dl=GUIDE&CFID=7912496&CFTOKEN=77115102
    # for a detailed description.
    #
    # For the Rubyist Traits can be explained in some short phrases quite
    effectively:
    #
    # * Traits are a Collection of Composable Behavior (e.g. methods)
    # They ressemble a lot to modules.
    t1 = trait{ def a; 42 end }
    assert_kind_of Trait, t1
    t2 = Trait::new { def b; 46 end }
    # * Traits can be composed by operations as + (union), &
    (intersection), * (aliasing)
    # and - (substraction). The result of such a trait composition is
    always a new trait.
    # As a matter of fact traits are immutable( with the exception of
    metaprogramming techniques ).
    t3 = t1 + t2
    assert_kind_of Trait, t3
    c1 = Class::new {
    use t3
    }
    assert_equal 42, c1.new.a
    assert_equal 46, c1.new.b
    # * When a trait is composed *and only in that case* conflicts might
    occur. Conflicts
    # can only occur during trait unification (that is +) in case the two unified
    # traits contain methods with the same name, these methods are
    replaced by conflict
    # raising methods. It is the responsability of the using entity
    (Module, Trait or Class)
    # to resolve the conflict.
    t4 = Trait::new do
    def a; :t4 end
    end
    t5 = Trait::new do
    def a; :t5 end
    end
    c1 = Class::new do
    use t4, t5 ### this is exactly the same as "use t4 + t5"
    end
    c2 = Class::new do
    use t4
    use t5 ### This is *not* the same as the above
    end
    assert_raise TraitsConflict do
    c1.new.a
    end
    assert_equal :t5, c2.new.a
    # The second case is distinct to the first in that a "use" of a trait is a
    # transparent operation in that the trait's methods are just defined as if they
    # were defined in the using entity (c2 in our example above).
    # Almost as if they were macroexpanded
    #
    # As mentioned above it is up to the using entity to resolve such
    conflicts, sometime
    # such conflict resolution needs access to the conflicting methods, in
    these cases
    # aliasing is our friend.
    t6 = trait do
    def the_answer; 22 end
    end
    t7 = trait do
    def the_answer; 20 end
    end
    c3 = Class::new {
    use t6 * {:first_answer => :the_answer} +
    t7 * {:second_answer=> :the_answer}
    def the_answer; first_answer + second_answer end
    }
    assert_equal 42, c3.new.the_answer
    # Sometimes of course we do not want to resolve the conflict
    explicitly but we just
    # want to avoid it by choising from which trait we want to use the method, again
    # trait composition shall be used to achieve that task.
    m1 = Module::new do
    use t6 + ( t7 - :the_answer )
    end
    assert_equal 22, Class::new{ extend m1 }.the_answer
    # * When traits are used they obey to the Flattening Property, that is
    the using entity
    # does not inherit from the trait but just get its methods defined.
    # Again one can very nicely understand what is going on when
    thinking of macro expansion.
    t= trait {
    def a; 1 end
    }
    c = Class::new {
    use t
    }
    o = c.new
    assert_equal 1, o.a
    t.send :add_method, :a do 2 end
    assert_equal 1, o.a
    assert_equal 1, c.new.a
    assert_equal 2, t.to_class.new.a
    # * Calls to super in trait methods are dynamically resolved at
    runtime, as is the case
    # for modules.
    t8 = trait { def a
    super * 7 end }
    c4 = Class::new { def a; 3 end }
    c5 = Class::new( c4 ) {
    use t8
    }
    assert_equal 21, c5.new.a
    # Again the flattening property can be tested with the semantics of
    super, the usage of the trait
    # just defines methods, they can be replaced with methods in the class
    later, but they are not
    # accessible via super, as they were if they had been included via modules.
    c5.module_eval do
    def a; super.succ end
    end
    assert_equal 4, c5.new.a

    -------------------------------------------------------------------------------------
    Ruby Traits
    -------------------------------------------------------------------------------------
    # Traits are Composable Units of Behavior. they are described here in detail
    # The interested can look here
    # http://portal.acm.org/ft_gateway.cf...=GUIDE&dl=GUIDE&CFID=7912496&CFTOKEN=77115102
    # for a detailed description.
    #
    # For the Rubyist Traits can be explained in some short phrases quite
    effectively:
    #
    # * Traits are a Collection of Composable Behavior (e.g. methods)
    # The ressemble a lot to modules.
    t1 = trait{ def a; 42 end }
    assert_kind_of Trait, t1
    t2 = Trait::new { def b; 46 end }
    # * Traits can be composed by operations as + (union), &
    (intersection), * (aliasing)
    # and - (substraction). The result of such a trait composition is
    always a new trait.
    # As a matter of fact traits are immutable( with the exception of
    metaprogramming techniques ).
    t3 = t1 + t2
    assert_kind_of Trait, t3
    c1 = Class::new {
    use t3
    }
    assert_equal 42, c1.new.a
    assert_equal 46, c1.new.b
    # * When a trait is composed *and only in that case* conflicts might
    occur. Conflicts
    # can only occur during trait unification (that is +) if and only if
    the two unified
    # traits contain methods with the same name, these methods are
    replaced by conflict
    # raising methods. It is the responsability of the using entity
    (Module, Trait or Class)
    # to resolve the conflict.
    t4 = Trait::new do
    def a; :t4 end
    end
    t5 = Trait::new do
    def a; :t5 end
    end
    c1 = Class::new do
    use t4, t5 ### this is exactly the same as "use t4 + t5"
    end
    c2 = Class::new do
    use t4
    use t5 ### This is *not* the same as the above
    end
    assert_raise TraitsConflict do
    c1.new.a
    end
    assert_equal :t5, c2.new.a
    # The second case is distinct to the first in that a "use" of a trait is a
    # transparent operation in which the trait's methods are just defined as if they
    # were defined in the using entity (c2 in our example above).
    #
    # As mentioned above it is up to the using entity to resolve such
    conflicts, sometime
    # such conflict resolution needs access to the conflicting methods, in
    these cases
    # aliasing is our friend.
    t6 = trait do
    def the_answer; 22 end
    end
    t7 = trait do
    def the_answer; 20 end
    end
    c3 = Class::new {
    use t6 * {:the_answer=>:first_answer} +
    t7 * {:the_answer=>:second_answer}
    def the_answer; first_answer + second_answer end
    }
    assert_equal 42, c3.new.the_answer
    # Sometimes of course we do not want to resolve the conflict
    explicitly but we just
    # want to avoid it by choising from which trait we want to use the method, again
    # trait composition shall be used to achieve that task.
    m1 = Module::new do
    use t6 + ( t7 - :the_answer )
    def the_answer; super.to_s.to_i(20) end
    end
    assert_equal 42, Class::new{ extend m1 }.the_answer
    # * When traits are used they obey to the Flattening Property, that is
    the using entity
    # does not inherit from the trait but just get its methods defined
    exactly as if the defs
    # of the trait were macro expanded into the using entity.
    # This however is something I have not implemented in ruby-traits
    (only in pure-traits).
    # As one can notice in the super example above the method
    t6#the_answer can be accessed via
    # super in the method m1#the_answer
    # There are two reasons for this, (a) I wanted to use Thomas'
    elegant and simple approach
    # and (b) this is a behaviour Rubyists are rather fond of, so I
    really thaught that
    # it might be a good idea to do this. The price to pay is that
    traits still behave a little
    # bit too much as Modules, the following example shows the down side
    of this approach
    t= trait {
    def a; 1 end
    }
    c = Class::new {
    use t
    }
    assert c < t
    o = c.new
    assert_equal 1, o.a
    t.send :add_method, :a do 2 end
    assert_equal 2, o.a
    # Another downside of this is that Trait#define_method,
    Trait#remove_method and friends
    # cannot be undefined, using them on traits directly would however
    endanger correct
    # conflict detection or trigger false conflicts!
    # In order to do correct traits metaprogramming Traits#add_method and
    Traits#del_method,
    # both are private, shall be used.
    # * Calls to super in trait methods are dynamically resolved at
    runtime, as is the case
    # for modules.
    # This even works together with the usage of super as shown above
    t8 = trait { def a; super * 7 end }
    c4 = Class::new { def a; 3 end }
    c5 = Class::new( c4 ) {
    use t8
    def a; super * 2 end
    }
    assert_equal 42, c5.new.a
    #
    # Trait#to_class simply is a convenience method that
    # simply creates an anonymous class from this trait.
    "t.to_class(parent=Object)" is short for
    # "Class::new(parent)"{ use t }
    t9 = trait { def a; :a9 end; def b; :b9 end }
    t10 = trait { def b; :b10 end; def c; :c10 end }
    c6 = ( t9 + t10 ).to_class
    assert_equal :a9, c6.new.a
    assert_equal :c10, c6.new.c
    assert_raise TraitsConflict do c6.new.b end

    -------------------------------------------------------------------------------------------------------
    Robert
    Robert Dober, Oct 23, 2007
    #1
    1. Advertising

  2. Robert Dober

    Trans Guest

    Re: Ruby-Traits 0.2 released (sorry long)

    On Oct 23, 4:16 pm, "Robert Dober" <> wrote:
    > Hi list
    >
    > after some very interesting input I decided to rewrite Ruby-Traits completely.
    >
    > http://rubyforge.org/frs/?group_id=4642&release_id=15737
    >
    > I have decided to implement two different kinds of traits:
    > * ruby-traits
    > and
    > * pure-traits.
    >
    > In detail:
    > * ruby-traits are mimicking some of the properties of traits but violate the
    > flattening property. They implement some of the more dynamic features
    > of modules and are in fact module based. The code is heavily influenced
    > by Thomas Sawyer's traits from Facets. Many thanks to Thomas for the
    > permission to use his code. Note that Thomas' traits of Facets is yet another
    > different beast.
    >
    > * pure-traits implement traits in the strict sense of their definition
    > here:http://portal.acm.org/ft_gateway.cfm?id=1028771&type=pdf&coll=GUIDE&d...
    >
    > They obey the flattening property, using a trait is exactly as defining
    > its methods inline, no modules are used and inheritance is unchanged.
    > All trait combination rules are obeyed (associative, communative, e.g.)
    >
    > The main purpose of pure-traits is to play around with them and to see the
    > benefits and shortcomings of traits based design.
    >
    > The syntax has changed, again inspired by Factes, trait aliasing is done
    > via #* now, thus traits aliased and combined can be expressed more
    > naturally:
    > t1 * { :t1_a => :a } + t2 * { :t2_a => :a }
    > works now as expected.
    >
    > In addition to the traditional trait operations +, - and *, I have
    > implemented & to allow of selective usage of methods.
    > I believe that the usage of this is normally a bad sign for the design
    > but sometimes it might be a handy feature.


    [snip]

    Whew! Yep, long, but interesting. I'll have to check the ruby-traits
    code and see if there's any cross-pollination to be had. Seems like
    maybe the main difference between this and Facets is the use of the
    Traits < Module class (where as Facets just extends Module). I'm also
    curious about the & method.

    Great work!

    T.
    Trans, Oct 24, 2007
    #2
    1. Advertising

  3. Robert Dober

    Robert Dober Guest

    Re: Ruby-Traits 0.2 released (sorry long)

    On 10/24/07, Trans <> wrote:
    >
    >
    > On Oct 23, 4:16 pm, "Robert Dober" <> wrote:
    > > Hi list
    > >
    > > after some very interesting input I decided to rewrite Ruby-Traits completely.
    > >
    > > http://rubyforge.org/frs/?group_id=4642&release_id=15737
    > >
    > > I have decided to implement two different kinds of traits:
    > > * ruby-traits
    > > and
    > > * pure-traits.
    > >
    > > In detail:
    > > * ruby-traits are mimicking some of the properties of traits but violate the
    > > flattening property. They implement some of the more dynamic features
    > > of modules and are in fact module based. The code is heavily influenced
    > > by Thomas Sawyer's traits from Facets. Many thanks to Thomas for the
    > > permission to use his code. Note that Thomas' traits of Facets is yet another
    > > different beast.
    > >
    > > * pure-traits implement traits in the strict sense of their definition
    > > here:http://portal.acm.org/ft_gateway.cfm?id=1028771&type=pdf&coll=GUIDE&d...
    > >
    > > They obey the flattening property, using a trait is exactly as defining
    > > its methods inline, no modules are used and inheritance is unchanged.
    > > All trait combination rules are obeyed (associative, communative, e.g.)
    > >
    > > The main purpose of pure-traits is to play around with them and to see the
    > > benefits and shortcomings of traits based design.
    > >
    > > The syntax has changed, again inspired by Factes, trait aliasing is done
    > > via #* now, thus traits aliased and combined can be expressed more
    > > naturally:
    > > t1 * { :t1_a => :a } + t2 * { :t2_a => :a }
    > > works now as expected.
    > >
    > > In addition to the traditional trait operations +, - and *, I have
    > > implemented & to allow of selective usage of methods.
    > > I believe that the usage of this is normally a bad sign for the design
    > > but sometimes it might be a handy feature.

    >
    > [snip]
    >
    > Whew! Yep, long, but interesting. I'll have to check the ruby-traits
    > code and see if there's any cross-pollination to be had. Seems like
    > maybe the main difference between this and Facets is the use of the
    > Traits < Module class (where as Facets just extends Module). I'm also
    > curious about the & method.
    >
    > Great work!

    Well thanks ;)
    to make your live a little bit easier, I decided to subclass Module
    because I needed some state for correct conflict detection and I did
    not want to pollute the Module instance variable space.
    AAMOF you can naively compute the conflicting methods of two modules like this

    m1.instance_methods & m2.instance_methods, however that will give
    false conflicts for the diamond shape inclusion:

    m = Module::new { def a; 42 end }
    m1 = m + Module::new{}
    m2 = m + Module::new{}
    m3 = m1 + m2 ## a is not a conflict

    In order to detect this I need state, at least I could not figure out
    how to do it dynamically, well walking up the ancestors array should
    work but I'd rather not go through that trouble. Hmm maybe I should...

    And & is just an idea stolen from Daniel and Mauricio ;)

    Cheers
    Robert
    Robert Dober, Oct 24, 2007
    #3
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. George Marsaglia

    Assigning unsigned long to unsigned long long

    George Marsaglia, Jul 8, 2003, in forum: C Programming
    Replies:
    1
    Views:
    645
    Eric Sosman
    Jul 8, 2003
  2. Daniel Rudy

    unsigned long long int to long double

    Daniel Rudy, Sep 19, 2005, in forum: C Programming
    Replies:
    5
    Views:
    1,161
    Peter Shaggy Haywood
    Sep 20, 2005
  3. Mathieu Dutour

    long long and long

    Mathieu Dutour, Jul 17, 2007, in forum: C Programming
    Replies:
    4
    Views:
    448
    santosh
    Jul 24, 2007
  4. ray
    Replies:
    1
    Views:
    1,291
    Robert Kern
    Jun 4, 2010
  5. Robert Dober

    [Ann] traits-1.9.1 released

    Robert Dober, Apr 24, 2008, in forum: Ruby
    Replies:
    0
    Views:
    83
    Robert Dober
    Apr 24, 2008
Loading...

Share This Page