Loading ...
Sorry, an error occurred while loading the content.

Mixins, traits and horizontal composition

Expand Messages
  • Mike Austin
    I ve been reading up on traits, or horizontal composition lately while working on Impulse s semantics. If anyone is interested, here are my conclusions: I
    Message 1 of 7 , May 8, 2010
    • 0 Attachment
      I've been reading up on traits, or horizontal composition lately while working on Impulse's semantics.  If anyone is interested, here are my conclusions:

      I used to think that Self traits, which are shared parent prototypes that contain only methods, were different from Squeak traits (aka Perl roles), which are used to compose classes.  But if my thinking is correct, Self traits are really an anonymous single Squeak trait.  Which, in turn, is a more well defined and implemented Ruby mixin.

      So on that note, I think traits can take the place of mixins, protocols (Obj-C) and even predicate classes, where an object can change behavior based on state.  If traits were an array of horizontal objects (versus vertical with inheritance), Impulse traits would look very similar to Ruby's mixins:

      trait <enumerable>

        method map: block
          local array = []

          self each: |object|
            array append: (block eval: object)
          end

          return array
        end

      end

      object <array>

        self.traits append: <enumerable>
       
        method each: |block|
          ...
        end

      end

      Predicate classes and the state pattern (UnrealScript states is a good example) can be emulated very easily by adding and removing traits dynamically, changing an object's behavior:

      object <game-object>

        field state

        method init
          self set-state: <walking>
        end
       
        method set-state: state
          self.traits remove: self.state
          self.state = state
          self.traits append: self.state
        end
       
      end

      trait <walking>

        method advance
          position += 1
        end

      end

      trait <running>

        method advance
          position += 3
        end

      end

      The main difference between traits and mixins, as I understand it, is that traits that have methods with the same name result in an error, whereas with a mixin, the method will just be overwritten.  Trait methods can be renamed to avoid conflicts.  Also, Ruby mixin methods are copied into the object itself, instead of the mixin being a referenced object.

      This shouldn't be that difficult to implement, if I can just allocate some time to work on it :)

      References:
      http://scg.unibe.ch/research/traits
      http://ruby-doc.org/core/classes/Enumerable.html

      Mike


    • Jecel Assumpcao Jr
      Mike, Adele Goldberg used the term traits when teaching Smalltalk-72 (which didn t have inheritance) to children so they could start to abstract the common
      Message 2 of 7 , May 8, 2010
      • 0 Attachment
        Mike,

        Adele Goldberg used the term "traits" when teaching Smalltalk-72 (which
        didn't have inheritance) to children so they could start to abstract the
        common features of groups of objects without getting bogged down in the
        complications of the concrete notion of classes.

        The Self people made all objects the same, but in practice three
        different roles appeared in practice. One was of "prototypes" to be used
        as templates for application objects (the second role - "normal"
        objects), and another was for parents of these prototypes to gather
        common behavior in a class-like role. They used the term "traits" to
        avoid confusion with the terms already used ("class", "superclass") but
        "mixins" was used as well.

        For Squeak, some features were added to mixins to make them more robust
        and it was felt that a different term was needed to call attention to
        this.

        None of these uses of "traits" are really related to each other.

        About dynamic inheritance - except for one demo of EmptySet and just Set
        nobody ever made a good use of it. I have been looking at a more
        restricted version where the parent would be set when the object is
        cloned but then would be constant during that object's lifetime (in
        theory, that is how the :self* slot in Self methods works).

        -- Jecel
      • Mike Austin
        ... Cool, I didn t know the use of the term traits went back this far. Good to know, thanks. ... I agree, they are not explicitly related, but they do have a
        Message 3 of 7 , May 9, 2010
        • 0 Attachment
          > Adele Goldberg used the term "traits" when teaching Smalltalk-72 (which
          > didn't have inheritance) to children so they could start to abstract the
          > common features of groups of objects without getting bogged down in the
          > complications of the concrete notion of classes.

          Cool, I didn't know the use of the term traits went back this far. Good to know, thanks.

          > None of these uses of "traits" are really related to each other.

          I agree, they are not explicitly related, but they do have a common function - grouping behavior. I'm attempting to combine the two - so that methods are not duplicated when cloning objects, and to also provide reusable named components.

          > About dynamic inheritance - except for one demo of EmptySet and just Set
          > nobody ever made a good use of it.

          I agree, dynamic inheritance has not proven itself. I think one problem is that it opens a can of worms and weird things can happen. I think dynamic traits would have less of a problem - first, because there is no state change involved, and second, traits are usually simpler. The state pattern I implemented with traits is not convoluted, and I think simpler than using delegation.

          > I have been looking at a more
          > restricted version where the parent would be set when the object is
          > cloned but then would be constant during that object's lifetime (in
          > theory, that is how the :self* slot in Self methods works).

          This seems reasonable. Self seems like a great language for experimentation, but in practice I think a little restriction is a good thing. Do you have any examples?

          Mike

          ________________________________
          From: Jecel Assumpcao Jr <jecel@...>
          To: langsmiths@yahoogroups.com
          Sent: Sat, May 8, 2010 2:09:22 PM
          Subject: Re: [langsmiths] Mixins, traits and horizontal composition


          Mike,

          Adele Goldberg used the term "traits" when teaching Smalltalk-72 (which
          didn't have inheritance) to children so they could start to abstract the
          common features of groups of objects without getting bogged down in the
          complications of the concrete notion of classes.

          The Self people made all objects the same, but in practice three
          different roles appeared in practice. One was of "prototypes" to be used
          as templates for application objects (the second role - "normal"
          objects), and another was for parents of these prototypes to gather
          common behavior in a class-like role. They used the term "traits" to
          avoid confusion with the terms already used ("class", "superclass" ) but
          "mixins" was used as well.

          For Squeak, some features were added to mixins to make them more robust
          and it was felt that a different term was needed to call attention to
          this.

          None of these uses of "traits" are really related to each other.



          About dynamic inheritance - except for one demo of EmptySet and just Set
          nobody ever made a good use of it. I have been looking at a more
          restricted version where the parent would be set when the object is
          cloned but then would be constant during that object's lifetime (in
          theory, that is how the :self* slot in Self methods works).

          -- Jecel
        • J. Baltasar García Perez-Schofield
          Hi, there! ... My project Zero: http://webs.uvigo.es/jbgarcia/prjs/zero/ Supports one prototype-based object oriented language and a class-based
          Message 4 of 7 , May 10, 2010
          • 0 Attachment
            Hi, there!

            > > I have been looking at a more
            > > restricted version where the parent would be set when the object is
            > > cloned but then would be constant during that object's lifetime (in
            > > theory, that is how the :self* slot in Self methods works).
            >
            > This seems reasonable. Self seems like a great language for experimentation, but in practice I think a little restriction is a good thing. Do you have any examples?

            My project Zero:

            http://webs.uvigo.es/jbgarcia/prjs/zero/

            Supports one prototype-based object oriented language and a class-based object-oriented language. The VM is a prototype-based, dynamic one.

            In Prowl, you can express dynamic inheritance this way:

            object MySet : EmptySet( size() == 0 )
            OneElementSet( size() == 1 ),
            NormalSet( size() > 1 )
            ;

            A "shadow method" is automatically generated, and when present, it is called every time an attribute of the object has changed. Its contents is a secquence of if's modelled by the inheritance yuntax expression.

            Only simple inheritance is available.

            Just my two-pence into the discussion.

            -- Baltasar García Perez-Schofield (jbgarcia@...)
            Dpt. Informática, Universidad de Vigo, España
            http://webs.uvigo.es/jbgarcia/
          • Jecel Assumpcao Jr
            ... My impression is that dynamic inheritance is too unstructured, like go to . It could be used to do interesting things, but its flexibility both makes it
            Message 5 of 7 , May 10, 2010
            • 0 Attachment
              Mike Austin wrote:

              > I agree, dynamic inheritance has not proven itself. I think one
              > problem is that it opens a can of worms and weird things can
              > happen. I think dynamic traits would have less of a problem -
              > first, because there is no state change involved, and second,
              > traits are usually simpler. The state pattern I implemented
              > with traits is not convoluted, and I think simpler than using delegation.

              My impression is that dynamic inheritance is too unstructured, like "go
              to". It could be used to do interesting things, but its flexibility both
              makes it harder to implement efficiently and makes the programmer's
              intention less clear.

              > > [alternative like :self*]
              >
              > This seems reasonable. Self seems like a great language for
              > experimentation, but in practice I think a little restriction is
              > a good thing. Do you have any examples?

              Are you asking for examples of how :self* is used in Self or of what I
              would do with such a feature?

              In the first case, how Self actually works is a lot closer to other
              Smalltalks than you might guess from the papers. But the story we tell
              the programmers is that a method is just a template for an activation
              object. So it has a slot for every argument. In the method (template),
              that slot has nil as a value but when it is cloned to create the
              activation these slots have their values set to the actual argument in
              the message. One of these arguments slots is always named "self" and is
              set to the receiver, but it also happens to be a parent slot. So any
              messages to "self" inside the method that don't correspond to argument
              or temporary variables get looked up in the receiver and its parents.
              Note that you could send the message 'self' to self if you need to refer
              to the receiver (though Self has a special bytecode for this it would
              work without it).

              Like I said, the actual implementation isn't like this at all. Instead
              the machine stack is used as much as possible and activation (context in
              Smalltalk terms) objects are faked when the debugger tries to use them.

              In the second case, if a language actually implemented this as a feature
              in a more general form so that cloning an object could take one or more
              arguments which would become parents of the new object then I would want
              graphic objects to "wrap" their models in a more flexible way than is
              now done in Morphic (in Self, I mean - for Squeak there is a hand
              crafted delegation framework to get this flexibility which is one reason
              why Morphic is such a monster there). This would combine nice features
              of Morphic and MVC: a single application model could have more than one
              graphical representation but each representation would actually *be* the
              application model.

              I didn't remember what the proper term was for wrapped objects, but a
              quick search for "composite object", "compound object" and "role object"
              didn't yield the results I wanted. Anyway, whatever it is called it is
              something I want to do and it would be nice for the language to support
              it.

              -- Jecel
            • Jecel Assumpcao Jr
              ... My impression is that dynamic inheritance is too unstructured, like go to . It could be used to do interesting things, but its flexibility both makes it
              Message 6 of 7 , May 10, 2010
              • 0 Attachment
                Mike Austin wrote:

                > I agree, dynamic inheritance has not proven itself. I think one
                > problem is that it opens a can of worms and weird things can
                > happen. I think dynamic traits would have less of a problem -
                > first, because there is no state change involved, and second,
                > traits are usually simpler. The state pattern I implemented
                > with traits is not convoluted, and I think simpler than using delegation.

                My impression is that dynamic inheritance is too unstructured, like "go
                to". It could be used to do interesting things, but its flexibility both
                makes it harder to implement efficiently and makes the programmer's
                intention less clear.

                > > [alternative like :self*]
                >
                > This seems reasonable. Self seems like a great language for
                > experimentation, but in practice I think a little restriction is
                > a good thing. Do you have any examples?

                Are you asking for examples of how :self* is used in Self or of what I
                would do with such a feature?

                In the first case, how Self actually works is a lot closer to other
                Smalltalks than you might guess from the papers. But the story we tell
                the programmers is that a method is just a template for an activation
                object. So it has a slot for every argument. In the method (template),
                that slot has nil as a value but when it is cloned to create the
                activation these slots have their values set to the actual argument in
                the message. One of these arguments slots is always named "self" and is
                set to the receiver, but it also happens to be a parent slot. So any
                messages to "self" inside the method that don't correspond to argument
                or temporary variables get looked up in the receiver and its parents.
                Note that you could send the message 'self' to self if you need to refer
                to the receiver (though Self has a special bytecode for this it would
                work without it).

                Like I said, the actual implementation isn't like this at all. Instead
                the machine stack is used as much as possible and activation (context in
                Smalltalk terms) objects are faked when the debugger tries to use them.

                In the second case, if a language actually implemented this as a feature
                in a more general form so that cloning an object could take one or more
                arguments which would become parents of the new object then I would want
                graphic objects to "wrap" their models in a more flexible way than is
                now done in Morphic (in Self, I mean - for Squeak there is a hand
                crafted delegation framework to get this flexibility which is one reason
                why Morphic is such a monster there). This would combine nice features
                of Morphic and MVC: a single application model could have more than one
                graphical representation but each representation would actually *be* the
                application model.

                I didn't remember what the proper term was for wrapped objects, but a
                quick search for "composite object", "compound object" and "role object"
                didn't yield the results I wanted. Anyway, whatever it is called it is
                something I want to do and it would be nice for the language to support
                it.

                -- Jecel
              • John Cowan
                ... It s also generally understood that traits don t have state: you create a class by listing its traits and its state variables. However, I ve been thinking
                Message 7 of 7 , Jun 17, 2010
                • 0 Attachment
                  Mike Austin scripsit:

                  > So on that note, I think traits can take the place of mixins, protocols
                  > (Obj-C) and even predicate classes, where an object can change behavior
                  > based on state. If traits were an array of horizontal objects (versus
                  > vertical with inheritance), Impulse traits would look very similar to
                  > Ruby's mixins:

                  It's also generally understood that traits don't have state: you create a
                  class by listing its traits and its state variables. However, I've been
                  thinking about an alternative in which all state variables are private to
                  a trait, the idea being that since state variables couple all methods
                  that reference them, such that none should be replaced without replacing
                  all, it makes sense to think of that set of methods and associated
                  state variables as a trait, and then horizontally compose classes out of
                  these state-bearing traits. Given that, it is no longer necessary to
                  distinguish between classes and traits: incorporating a class is
                  equivalent to incorporating the traits that the class incorporates.

                  --
                  Is not a patron, my Lord [Chesterfield], John Cowan
                  one who looks with unconcern on a man http://www.ccil.org/~cowan
                  struggling for life in the water, and when cowan@...
                  he has reached ground encumbers him with help?
                  --Samuel Johnson
                Your message has been successfully submitted and would be delivered to recipients shortly.