Index: Makefile.in =================================================================== diff -u -r24724ebae83af4e0104b349a2fb582bfc71a7475 -r444fa56b72c6d35bd3cbbe46a44b12a4ea33088f --- Makefile.in (.../Makefile.in) (revision 24724ebae83af4e0104b349a2fb582bfc71a7475) +++ Makefile.in (.../Makefile.in) (revision 444fa56b72c6d35bd3cbbe46a44b12a4ea33088f) @@ -504,6 +504,8 @@ $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-serialization.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-singleton.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_doc_dir_native)/example-scripts/rosetta-unknown-method.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) + $(TCLSH) $(src_doc_dir_native)/example-scripts/traits-composite.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) + $(TCLSH) $(src_doc_dir_native)/example-scripts/traits-simple.tcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) test-xotcl: $(TCLSH_PROG) $(TCLSH) $(xotcl_src_test_dir)/testo.xotcl -libdir $(PLATFORM_DIR) $(TESTFLAGS) Index: doc/example-scripts/traits-composite.html =================================================================== diff -u --- doc/example-scripts/traits-composite.html (revision 0) +++ doc/example-scripts/traits-composite.html (revision 444fa56b72c6d35bd3cbbe46a44b12a4ea33088f) @@ -0,0 +1,899 @@ + + + + + +Listing of doc/example-scripts/traits-composite.tcl + + + + + +
+
+
+

Implementation study based on

+
    +
  1. +

    +Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: +Traits: A Mechanism for Fine-grained Reuse, +ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 +

    +
  2. +
+

Fig 13: TReadStream and TWriteStream as composite traits

+

In this example, traits are used to extend classes and other traits +(called then composite traits).

+
+
+
package req nx::trait
+

Create a Trait called TPositionableStream

+
+
+
nx::Trait create TPositionableStream {
+  #
+  # Define the methods provided by this trait:
+  #
+  :public method atStart {} {expr {[:position] == [:minPosition]}}
+  :public method atEnd {} {expr {[:position] == [:maxPosition]}}
+  :public method setToStart {} {set :position [:minPosition]}
+  :public method setToEnd {} {set :position [:maxPosition]}
+  :public method maxPosition {} {llength ${:collection}}
+  :public method minPosition {} {return 0}
+  :public method nextPosition {} {incr :position 1}
+
+  # The trait requires a method "position" and a variable "collection"
+  # from the base class or other traits. The definition is incomplete
+  # in these regards
+
+  :requiredMethods position
+  :requiredVariables collection
+}
+

Create a composite trait called TReadStream based on the trait +TPositionableStream:

+
+
+
nx::Trait create TReadStream {
+  #
+  # Methods provided by this trait:
+  #
+  :public method on {collection} {set :collection $collection; :setToStart}
+  :public method next {} {
+    if {[:atEnd]} {return ""} else {
+      set r [lindex ${:collection} ${:position}]
+      :nextPosition
+      return $r
+    }
+  }
+
+  # This trait requires these methods:
+  :requiredMethods {setToStart atEnd nextPosition}
+
+  # Use the trait "TPositionableStream"
+  :useTrait TPositionableStream
+}
+

Create a composite trait called TWriteStream based on the trait +TPositionableStream:

+
+
+
nx::Trait create TWriteStream {
+  #
+  # Methods provided by this trait:
+  #
+  :public method on {collection} {set :collection $collection; :setToEnd}
+  :public method nextPut {element} {
+    lappend :collection $element
+    :nextPosition
+    return ""
+  }
+
+  # This trait requires these methods:
+  :requiredMethods {setToEnd nextPosition}
+
+  # Use the trait "TPositionableStream"
+  :useTrait TPositionableStream
+}
+

Define a class ReadStream with properties position and +collection that uses the composite trait TReadStream:

+
+
+
nx::Class create ReadStream {
+  :property {collection ""}
+  :property {position 0}
+  :useTrait TReadStream
+}
+

Create an instance of ReadStream:

+
+
+
ReadStream create r1 -collection {a b c d e}
+

Test the behavior of the composed class:

+
+
+
% r1 atStart
+1
+% r1 atEnd
+0
+% r1 next
+a
+% r1 next
+b
+
+
+
+

+ + + Index: doc/example-scripts/traits-composite.tcl =================================================================== diff -u --- doc/example-scripts/traits-composite.tcl (revision 0) +++ doc/example-scripts/traits-composite.tcl (revision 444fa56b72c6d35bd3cbbe46a44b12a4ea33088f) @@ -0,0 +1,99 @@ +# Implementation study based on +# +# S. Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: +# Traits: A Mechanism for Fine-grained Reuse, +# ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 +# +# Fig 13: TReadStream and TWriteStream as composite traits +# +# In this example, traits are used to extend classes and other traits +# (called then _composite traits_). +# +package req nx::test +package req nx::trait + +# +# Create a Trait called +TPositionableStream+ +# +nx::Trait create TPositionableStream { + # + # Define the methods provided by this trait: + # + :public method atStart {} {expr {[:position] == [:minPosition]}} + :public method atEnd {} {expr {[:position] == [:maxPosition]}} + :public method setToStart {} {set :position [:minPosition]} + :public method setToEnd {} {set :position [:maxPosition]} + :public method maxPosition {} {llength ${:collection}} + :public method minPosition {} {return 0} + :public method nextPosition {} {incr :position 1} + + # The trait requires a method "position" and a variable "collection" + # from the base class or other traits. The definition is incomplete + # in these regards + + :requiredMethods position + :requiredVariables collection +} + +# +# Create a composite trait called +TReadStream+ based on the trait +# +TPositionableStream+: +# +nx::Trait create TReadStream { + # + # Methods provided by this trait: + # + :public method on {collection} {set :collection $collection; :setToStart} + :public method next {} { + if {[:atEnd]} {return ""} else { + set r [lindex ${:collection} ${:position}] + :nextPosition + return $r + } + } + + # This trait requires these methods: + :requiredMethods {setToStart atEnd nextPosition} + + # Use the trait "TPositionableStream" + :useTrait TPositionableStream +} + +# +# Create a composite trait called +TWriteStream+ based on the trait +# +TPositionableStream+: +# +nx::Trait create TWriteStream { + # + # Methods provided by this trait: + # + :public method on {collection} {set :collection $collection; :setToEnd} + :public method nextPut {element} { + lappend :collection $element + :nextPosition + return "" + } + + # This trait requires these methods: + :requiredMethods {setToEnd nextPosition} + + # Use the trait "TPositionableStream" + :useTrait TPositionableStream +} + +# Define a class +ReadStream+ with properties +position+ and +# +collection+ that uses the composite trait +TReadStream+: +nx::Class create ReadStream { + :property {collection ""} + :property {position 0} + :useTrait TReadStream +} + +# Create an instance of +ReadStream+: +ReadStream create r1 -collection {a b c d e} + +# Test the behavior of the composed class: +? {r1 atStart} 1 +? {r1 atEnd} 0 +? {r1 next} a +? {r1 next} b Index: doc/example-scripts/traits-simple.html =================================================================== diff -u --- doc/example-scripts/traits-simple.html (revision 0) +++ doc/example-scripts/traits-simple.html (revision 444fa56b72c6d35bd3cbbe46a44b12a4ea33088f) @@ -0,0 +1,846 @@ + + + + + +Listing of doc/example-scripts/traits-simple.tcl + + + + + +
+
+
+

Implementation study based on

+
    +
  1. +

    +Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: + Traits: A Mechanism for Fine-grained Reuse, + ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 +

    +
  2. +
+

Example in Fig 12: ReadStream and Trait TReadStream

+

In this example, traits are used to extend classes and other traits.

+
+
+
package require nx::trait
+

Create a simple trait called TReadStream which provides the +interface to a stream. In contrary to a composite trait, a simple +trait does not inherit from another trait.

+
+
+
nx::Trait create TReadStream {
+  #
+  # Define the methods provided by this trait:
+  #
+  :public method atStart {} {expr {[:position] == [:minPosition]}}
+  :public method atEnd {} {expr {[:position] == [:maxPosition]}}
+  :public method setToStart {} {set :position [:minPosition]}
+  :public method setToEnd {} {set :position [:maxPosition]}
+  :public method maxPosition {} {llength ${:collection}}
+  :public method on {collection} {set :collection $collection; :setToStart}
+  :public method next {} {
+    if {[:atEnd]} {return ""} else {
+      set r [lindex ${:collection} ${:position}]
+      :nextPosition
+      return $r
+    }
+  }
+  :public method minPosition {} {return 0}
+  :public method nextPosition {} {incr :position 1}
+
+  # This trait requires a method "position" and a variable
+  # "collection" from the base class. The definition is incomplete in
+  # these regards.
+  :requiredMethods position
+  :requiredVariables collection
+}
+

Define the class ReadStream with properties position and +collection that uses the trait. The method useTrait checks the +requirements of the trait and imports the methods into ReadStream.

+
+
+
nx::Class create ReadStream {
+  :property {collection ""}
+  :property {position 0}
+  :useTrait TReadStream
+}
+

Create an instance of the class ReadStream:

+
+
+
ReadStream create r1 -collection {a b c d e}
+

Test the behavior of the composed class:

+
+
+
% r1 atStart
+1
+% r1 atEnd
+0
+% r1 next
+a
+% r1 next
+b
+
+
+
+

+ + + Index: doc/example-scripts/traits-simple.tcl =================================================================== diff -u --- doc/example-scripts/traits-simple.tcl (revision 0) +++ doc/example-scripts/traits-simple.tcl (revision 444fa56b72c6d35bd3cbbe46a44b12a4ea33088f) @@ -0,0 +1,64 @@ +# Implementation study based on +# +# S. Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: +# Traits: A Mechanism for Fine-grained Reuse, +# ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 +# +# Example in Fig 12: ReadStream and Trait TReadStream +# +# In this example, traits are used to extend classes and other traits. + +package require nx::test +package require nx::trait + +# +# Create a simple trait called +TReadStream+ which provides the +# interface to a stream. In contrary to a composite trait, a simple +# trait does not inherit from another trait. +# +nx::Trait create TReadStream { + # + # Define the methods provided by this trait: + # + :public method atStart {} {expr {[:position] == [:minPosition]}} + :public method atEnd {} {expr {[:position] == [:maxPosition]}} + :public method setToStart {} {set :position [:minPosition]} + :public method setToEnd {} {set :position [:maxPosition]} + :public method maxPosition {} {llength ${:collection}} + :public method on {collection} {set :collection $collection; :setToStart} + :public method next {} { + if {[:atEnd]} {return ""} else { + set r [lindex ${:collection} ${:position}] + :nextPosition + return $r + } + } + :public method minPosition {} {return 0} + :public method nextPosition {} {incr :position 1} + + # This trait requires a method "position" and a variable + # "collection" from the base class. The definition is incomplete in + # these regards. + + :requiredMethods position + :requiredVariables collection +} + +# Define the class +ReadStream+ with properties +position+ and +# +collection+ that uses the trait. The method +useTrait+ checks the +# requirements of the trait and imports the methods into +ReadStream+. + +nx::Class create ReadStream { + :property {collection ""} + :property {position 0} + :useTrait TReadStream +} + +# Create an instance of the class +ReadStream+: +ReadStream create r1 -collection {a b c d e} + +# Test the behavior of the composed class: +? {r1 atStart} 1 +? {r1 atEnd} 0 +? {r1 next} a +? {r1 next} b Index: library/lib/nx-traits.tcl =================================================================== diff -u --- library/lib/nx-traits.tcl (revision 0) +++ library/lib/nx-traits.tcl (revision 444fa56b72c6d35bd3cbbe46a44b12a4ea33088f) @@ -0,0 +1,78 @@ +package require nx +package provide nx::trait 0.1 + +# @package nx::trait +# +# Minimal trait framework with checking in NX, based on +# +# S. Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: +# Traits: A Mechanism for Fine-grained Reuse, +# ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 +# +# Gustaf Neumann (Aug 2011) +# +# Traits are a mechanism for the reuse of methods. In contrary to +# other forms of reuse (e.g. inheritance of methods in a class +# hierarchy or via mixin classes), the methods defined in traits are +# materialized in the target objects and classes. This gives more +# fine-grained control over the reuse of methods and overcomes the +# "total composition ordering" limitation of mixins. +# +# The current implementation does not handle overwrites (conflicting +# definition from several traits), be we handle renames (aliases) and +# we check required methods. "requiredVariables" (not part of the +# ducasse paper) are not checked yet. +# +# In essence, the package provides a class "nx::Trait" to define +# Traits and a method "useTrait" to reuse a trait in trait consumer +# (e.g. a class or another trait). +# +# Usage: +# package require nx::trait +# nx::Trait create .... { +# ... +# } +# nx::Class create .... { +# ... +# :useTrait ... +# } +# + +nsf::proc nx::addTrait {obj traitName {nameMap ""}} { + array set map $nameMap + foreach m [$traitName info methods] { + if {[info exists map($m)]} {set newName $map($m)} else {set newName $m} + $obj public alias $newName [$traitName info method handle $m] + } +} + +nx::Class public method useTrait {traitName {nameMap ""}} { + # adding a trait to a class + set obj [:new] + foreach m [$traitName requiredMethods] { + # it would be nice to have a ":info methods -closure $m", we would not have to instantiate the class. + #puts "$m ok? [:info methods -closure $m]" + if {[$obj info lookup method $m] eq ""} { + error "trait $traitName requires $m, which is not defined" + } + } + $obj destroy + nx::addTrait [self] $traitName $nameMap +} + +nx::Class create nx::Trait { + + :property {requiredMethods:0..n,incremental ""} + :property {requiredVariables:0..n,incremental ""} + + :public method useTrait {traitName {nameMap ""}} { + # adding a trait to a trait + nx::addTrait [self] $traitName $nameMap + set finalReqMethods {} + foreach m [lsort -unique [concat ${:requiredMethods} [$traitName requiredMethods]]] { + if {[:info lookup method $m] eq ""} {lappend finalReqMethods $m} + } + puts "final reqMethods of [self]: $finalReqMethods // defined=[:info methods]" + set :requiredMethods $finalReqMethods + } +} \ No newline at end of file