12. Nixon’s Diamond
Quakers are Pacifists, mostly.
Republicans are not Pacifists, mostly.
Nixon is both a Quaker and a Republican, certainly.
Is Nixon a pacifist?
15. Mixins:
Linearity
module A
def as_string
super + ' Mixin-A'
end
end
module B
def as_string
super + ' Mixin-B'
end
end
class BaseA
def as_string
'BaseA'
end
end
class MyA < BaseA
include B
include A
end
$stderr.puts MyA.new.as_string
meson10@xps:~/workspace/meson10/traits$ ruby super.rb
BaseA Mixin-B Mixin-A
22. Traits
Specification
require ‘cube’
Adder = Cube.interface {
proto(:sum, [Integer]) { Integer }
}
# Expect methods for parameters.
ProductCalcT = Cube.trait do
def product(a, b)
ret = 0
a.times { ret = sum([ret, b]) }
ret
end
# Enforce input.
requires_interface Adder
end
StatsCalcT = Cube.trait do
def avg(arr)
arr.reduce(0, &:+) / arr.size
end
end
23. Traits
Creating
Classes
Class = Superclass + State + Traits + Glue
SimpleCalc = Calc.with_trait(ProductCalcT)
sc = SimpleCalc.new
p sc.product(3, 2)
Or
AdvancedCalc = Calc
.with_trait(ProductCalcT)
.with_trait(StatsCalcT)
ac = AdvancedCalc.new
p ac.product(3, 2)
p ac.avg([3, 2, 4])
24. Traits
Conflict
Resolution [⅓]
ProductCalcT = Cube.trait do
def product(a, b)
ret = 0
a.times { ret = sum([ret, b]) }
ret
end
requires_interface Adder
end
StatsCalcT = Cube.trait do
def product; end
def avg(arr)
arr.reduce(0, &:+) / arr.size
end
end
AdvancedCalc = Calc.with_trait(ProductCalcT).with_trait(StatsCalcT)
AdvancedCalc.new.product(3, 2)
meson10@xps:$ bundle exec ruby traits.rb
/home/meson10/cube/traits.rb:38:in `append_features':
(Cube::Trait::MethodConflict)
#<UnboundMethod:
#<Class:0x005642a0cad580>(#<Module:0x005642a0cb5870>)#product>
25. ProductCalcT = Cube.trait do
def product(a, b)
ret = 0
a.times { ret = sum([ret, b]) }
ret
end
requires_interface Adder
end
StatsCalcT = Cube.trait do
def product; end
def avg(arr)
arr.reduce(0, &:+) / arr.size
end
end
AdvancedCalc = Calc.with_trait(ProductCalcT)
.with_trait(StatsCalcT, suppress: [:product])
P AdvancedCalc.new.product(3, 2)
meson10@xps:$ bundle exec ruby traits.rb
6
Traits
Conflict
Resolution [⅔]
26. class CalcImpl
def sum(a)
a.reduce(0, &:+)
end
# Trumps trait.
def product(a, b)
8
end
end
Calc = Cube.from(CalcImpl)
ProductCalcT = Cube.trait do
def product(a, b)
ret = 0
a.times { ret = sum([ret, b]) }
ret
end
requires_interface Adder
end
SimpleCalc = Calc.with_trait(ProductCalcT)
p SimpleCalc.new.product(3, 2)
Traits
Conflict
Resolution
[3/3]
27. ProductCalcT = Cube.trait do
def product(a, b)
ret = 0
a.times { ret = sum([ret, b]) }
ret
end
requires_interface Adder
end
PolarCalcT = Cube.trait do
def product(a, b)
# Polar implementation
88
end
end
AdvancedCalc = Calc
.with_trait(ProductCalcT)
.with_trait(PolarCalcT, rename: {"product" => "polar_product"})
ac = AdvancedCalc.new
p ac.product(3, 2)
p ac.polar_product(3, 2)
Traits
Aliasing
29. Traits
Issue(s)
When two traits are composed, it may be that each
requires a semantically different method that
happens to have the same name.
Consider a trait X that uses two traits Y1 and Y2 ,
which in turn both use the trait Z . foo provided
by Z will be obtained by X twice. The key
language design question is: should this be
considered a conflict?
A trait contains methods that implement the behaviour that it provides.
A trait may require methods as parameters for the provided
Behaviour.
Traits cannot specify any state, and never access state directly.
Trait methods can access state indirectly, using required methods.
The purpose of traits is to decompose classes into reusable building blocks by pro-
viding first-class representations for the different aspects of the behaviour of a class.
Note that we use the term “aspect” to denote an independent, but not necessarily cross-
cutting, concern. Traits differ from classes in that they do not define any kind of state,
and that they can be composed using mechanisms other than inheritance.