algebrick 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -3
- data/README_FULL.md +13 -21
- data/VERSION +1 -1
- data/doc/actor.rb +21 -0
- data/doc/data.in.rb +99 -0
- data/doc/data.out.rb +103 -0
- data/doc/extending_behavior.in.rb +61 -0
- data/doc/extending_behavior.out.rb +62 -0
- data/doc/format.rb +75 -0
- data/doc/init.rb +1 -0
- data/doc/json.in.rb +39 -0
- data/doc/json.out.rb +43 -0
- data/doc/null.in.rb +36 -0
- data/doc/null.out.rb +40 -0
- data/doc/parametrized.in.rb +37 -0
- data/doc/parametrized.out.rb +41 -0
- data/doc/pattern_matching.in.rb +116 -0
- data/doc/pattern_matching.out.rb +122 -0
- data/doc/quick_example.in.rb +27 -0
- data/doc/quick_example.out.rb +27 -0
- data/doc/tree1.in.rb +10 -0
- data/doc/tree1.out.rb +10 -0
- data/doc/type_def.in.rb +21 -0
- data/doc/type_def.out.rb +21 -0
- data/doc/values.in.rb +52 -0
- data/doc/values.out.rb +58 -0
- data/lib/algebrick/atom.rb +49 -0
- data/lib/algebrick/dsl.rb +104 -0
- data/lib/algebrick/field_method_readers.rb +43 -0
- data/lib/algebrick/matcher_delegations.rb +45 -0
- data/lib/algebrick/matchers/abstract.rb +127 -0
- data/lib/algebrick/matchers/abstract_logic.rb +38 -0
- data/lib/algebrick/matchers/and.rb +29 -0
- data/lib/algebrick/matchers/any.rb +37 -0
- data/lib/algebrick/matchers/array.rb +57 -0
- data/lib/algebrick/matchers/atom.rb +28 -0
- data/lib/algebrick/matchers/not.rb +44 -0
- data/lib/algebrick/matchers/or.rb +51 -0
- data/lib/algebrick/matchers/product.rb +73 -0
- data/lib/algebrick/matchers/variant.rb +29 -0
- data/lib/algebrick/matchers/wrapper.rb +57 -0
- data/lib/algebrick/matchers.rb +31 -0
- data/lib/algebrick/matching.rb +62 -0
- data/lib/algebrick/parametrized_type.rb +122 -0
- data/lib/algebrick/product_constructors/abstract.rb +70 -0
- data/lib/algebrick/product_constructors/basic.rb +47 -0
- data/lib/algebrick/product_constructors/named.rb +58 -0
- data/lib/algebrick/product_constructors.rb +25 -0
- data/lib/algebrick/product_variant.rb +195 -0
- data/lib/algebrick/reclude.rb +39 -0
- data/lib/algebrick/serializer.rb +129 -0
- data/lib/algebrick/serializers.rb +25 -0
- data/lib/algebrick/type.rb +61 -0
- data/lib/algebrick/type_check.rb +58 -0
- data/lib/algebrick/types.rb +59 -0
- data/lib/algebrick/value.rb +41 -0
- data/lib/algebrick.rb +14 -1170
- data/spec/algebrick_test.rb +708 -0
- metadata +105 -27
@@ -0,0 +1,37 @@
|
|
1
|
+
# Let's define Tree but this time parameterized by :value_type,
|
2
|
+
Tree = Algebrick.type(:value_type) do |tree|
|
3
|
+
variants Empty = atom,
|
4
|
+
Leaf = type(:value_type) { fields value: :value_type },
|
5
|
+
Node = type(:value_type) { fields left: tree, right: tree }
|
6
|
+
end
|
7
|
+
|
8
|
+
# with method depth defined as before.
|
9
|
+
module Tree
|
10
|
+
def depth
|
11
|
+
match self,
|
12
|
+
Empty >> 0,
|
13
|
+
Leaf >> 1,
|
14
|
+
Node.(~any, ~any) >-> left, right do
|
15
|
+
1 + [left.depth, right.depth].max
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end #
|
19
|
+
|
20
|
+
# Then Tree, Leaf, Node are
|
21
|
+
Tree.class
|
22
|
+
[Tree, Leaf, Node]
|
23
|
+
# which servers as factories to actual types.
|
24
|
+
Tree[Float]
|
25
|
+
Tree[String]
|
26
|
+
|
27
|
+
# Types cannot be mixed.
|
28
|
+
Leaf[Integer]['1'] rescue $!
|
29
|
+
Node[Integer][Leaf[String]['a'], Empty] rescue $!
|
30
|
+
Leaf[String]['1']
|
31
|
+
|
32
|
+
# Depth method works as before.
|
33
|
+
integer_tree = Node[Integer][Leaf[Integer][2], Empty]
|
34
|
+
integer_tree.depth
|
35
|
+
string_tree = Node[String][Empty, Empty]
|
36
|
+
string_tree.depth
|
37
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Let's define Tree but this time parameterized by :value_type,
|
2
|
+
Tree = Algebrick.type(:value_type) do |tree|
|
3
|
+
variants Empty = atom,
|
4
|
+
Leaf = type(:value_type) { fields value: :value_type },
|
5
|
+
Node = type(:value_type) { fields left: tree, right: tree }
|
6
|
+
end
|
7
|
+
|
8
|
+
# with method depth defined as before.
|
9
|
+
module Tree
|
10
|
+
def depth
|
11
|
+
match self,
|
12
|
+
Empty >> 0,
|
13
|
+
Leaf >> 1,
|
14
|
+
Node.(~any, ~any) >-> left, right do
|
15
|
+
1 + [left.depth, right.depth].max
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Then Tree, Leaf, Node are
|
21
|
+
Tree.class # => Algebrick::ParametrizedType
|
22
|
+
[Tree, Leaf, Node]
|
23
|
+
# => [Tree[value_type], Leaf[value_type], Node[value_type]]
|
24
|
+
# which servers as factories to actual types.
|
25
|
+
Tree[Float] # => Tree[Float](Empty | Leaf[Float] | Node[Float])
|
26
|
+
Tree[String] # => Tree[String](Empty | Leaf[String] | Node[String])
|
27
|
+
|
28
|
+
# Types cannot be mixed.
|
29
|
+
Leaf[Integer]['1'] rescue $!
|
30
|
+
# => #<TypeError: Value (String) '1' is not any of: Integer.>
|
31
|
+
Node[Integer][Leaf[String]['a'], Empty] rescue $!
|
32
|
+
# => #<TypeError: Value (Leaf[String](value: String)) 'Leaf[String][value: a]' is not any of: Tree[Integer](Empty | Leaf[Integer] | Node[Integer]).>
|
33
|
+
Leaf[String]['1'] # => Leaf[String][value: 1]
|
34
|
+
|
35
|
+
# Depth method works as before.
|
36
|
+
integer_tree = Node[Integer][Leaf[Integer][2], Empty]
|
37
|
+
# => Node[Integer][left: Leaf[Integer][value: 2], right: Empty]
|
38
|
+
integer_tree.depth # => 2
|
39
|
+
string_tree = Node[String][Empty, Empty] # => Node[String][left: Empty, right: Empty]
|
40
|
+
string_tree.depth # => 1
|
41
|
+
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# Let's define a tree and binary tree to demonstrate the pattern matching abilities.
|
2
|
+
Tree = Algebrick.type do |tree|
|
3
|
+
variants Empty = type,
|
4
|
+
Leaf = type { fields Integer },
|
5
|
+
Node = type { fields tree, tree }
|
6
|
+
end
|
7
|
+
|
8
|
+
BinaryTree = BTree = Algebrick.type do |btree|
|
9
|
+
fields! value: Comparable, left: btree, right: btree
|
10
|
+
variants Empty, btree
|
11
|
+
end
|
12
|
+
|
13
|
+
extend Algebrick::Matching
|
14
|
+
|
15
|
+
# Product matchers are constructed with #.() syntax.
|
16
|
+
Leaf.(any) === Leaf[1]
|
17
|
+
Leaf.(1) === Leaf[1]
|
18
|
+
Leaf.(2) === Leaf[1]
|
19
|
+
# There are also some shortcuts to use when product has more fields.
|
20
|
+
BTree.()
|
21
|
+
BTree.(value: any, left: Empty)
|
22
|
+
BTree.(value: any, left: Empty) === BTree[1, Empty, Empty]
|
23
|
+
|
24
|
+
# Any object responding to #=== can be converted to matcher.
|
25
|
+
(1..2).to_m
|
26
|
+
(1..2).to_m === 2
|
27
|
+
Empty.to_m
|
28
|
+
# As matchers are using standard #=== method it does not have to be always converted.
|
29
|
+
Empty === Empty
|
30
|
+
Leaf === Leaf[1]
|
31
|
+
|
32
|
+
# Tree matches all its values.
|
33
|
+
[Empty, Leaf[1], Node[Empty, Empty]].all? { |v| Tree === v }
|
34
|
+
|
35
|
+
# There is also a #match method in Matching module to make pattern matching easier.
|
36
|
+
match Leaf[1], # supply the value for matching
|
37
|
+
# if Leaf.(0) matches :zero is returned
|
38
|
+
(on Leaf.(0), :zero),
|
39
|
+
# when computation of the result needs to be avoided use block
|
40
|
+
# if Leaf.(1) matches block is called and its result is returned
|
41
|
+
(on Leaf.(1) do
|
42
|
+
(1..10000).inject(:*) # expensive computation
|
43
|
+
:one # which is :one in this case
|
44
|
+
end)
|
45
|
+
|
46
|
+
# Alternatively case can be used.
|
47
|
+
case Leaf[1]
|
48
|
+
when Leaf.(0)
|
49
|
+
:zero
|
50
|
+
when Leaf.(1)
|
51
|
+
(1..10000).inject(:*) # expensive computation
|
52
|
+
:one
|
53
|
+
end
|
54
|
+
|
55
|
+
# But that won't work nicely with value deconstruction.
|
56
|
+
# Each matcher can be marked with #~ method to store value against which is being matched,
|
57
|
+
# each matched value is passed to the block, ...
|
58
|
+
match Leaf[0],
|
59
|
+
(on ~Leaf.(~any) do |leaf, value|
|
60
|
+
[leaf, value]
|
61
|
+
end)
|
62
|
+
|
63
|
+
btree = BTree[1,
|
64
|
+
BTree[0, Empty, Empty],
|
65
|
+
Empty]
|
66
|
+
match btree,
|
67
|
+
(on BTree.(any, ~any, ~any) do |left, right|
|
68
|
+
[left, right]
|
69
|
+
end)
|
70
|
+
|
71
|
+
# or alternatively you can use Ruby's multi-assignment feature.
|
72
|
+
match btree,
|
73
|
+
(on ~BTree do |(_, left, right)|
|
74
|
+
[left, right]
|
75
|
+
end)
|
76
|
+
|
77
|
+
|
78
|
+
# Matchers also support logical operations #& for and, #| for or, and #! for negation.
|
79
|
+
Color = Algebrick.type do
|
80
|
+
variants Black = atom,
|
81
|
+
White = atom,
|
82
|
+
Pink = atom,
|
83
|
+
Grey = type { fields scale: Float }
|
84
|
+
end
|
85
|
+
|
86
|
+
def color?(color)
|
87
|
+
match color,
|
88
|
+
on(Black | Grey.(-> v { v < 0.2 }), 'black-ish'),
|
89
|
+
on(White | Grey.(-> v { v > 0.8 }), 'white-ish'),
|
90
|
+
on(Grey.(-> v { v >= 0.2 }) & Grey.(-> v { v <= 0.8 }), 'grey-ish'),
|
91
|
+
on(Pink, "that's not a color ;)")
|
92
|
+
end
|
93
|
+
|
94
|
+
color? Black
|
95
|
+
color? Grey[0.1]
|
96
|
+
color? Grey[0.3]
|
97
|
+
color? Grey[0.9]
|
98
|
+
color? White
|
99
|
+
color? Pink
|
100
|
+
|
101
|
+
# A more complicated example of extracting node's value and values of its left and right side
|
102
|
+
# using also logical operators to allow Empty sides.
|
103
|
+
match BTree[0, Empty, BTree[1, Empty, Empty]],
|
104
|
+
(on BTree.({ value: ~any,
|
105
|
+
left: Empty | BTree.(value: ~any),
|
106
|
+
right: Empty | BTree.(value: ~any) }) do |value, left, right|
|
107
|
+
{ left: left, value: value, right: right }
|
108
|
+
end)
|
109
|
+
|
110
|
+
# There is also a more funky syntax for matching
|
111
|
+
# using #>, #>> and Ruby 1.9 syntax for lambdas `-> {}`.
|
112
|
+
match Leaf[1],
|
113
|
+
Leaf.(0) >> :zero,
|
114
|
+
Leaf.(~any) >-> value do
|
115
|
+
(1..value).inject(:*) # an expensive computation
|
116
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# Let's define a tree and binary tree to demonstrate the pattern matching abilities.
|
2
|
+
Tree = Algebrick.type do |tree|
|
3
|
+
variants Empty = type,
|
4
|
+
Leaf = type { fields Integer },
|
5
|
+
Node = type { fields tree, tree }
|
6
|
+
end # => Tree(Empty | Leaf | Node)
|
7
|
+
|
8
|
+
BinaryTree = BTree = Algebrick.type do |btree|
|
9
|
+
fields! value: Comparable, left: btree, right: btree
|
10
|
+
variants Empty, btree
|
11
|
+
end
|
12
|
+
# => BTree(Empty | BTree(value: Comparable, left: BTree, right: BTree))
|
13
|
+
|
14
|
+
extend Algebrick::Matching # => main
|
15
|
+
|
16
|
+
# Product matchers are constructed with #.() syntax.
|
17
|
+
Leaf.(any) === Leaf[1] # => true
|
18
|
+
Leaf.(1) === Leaf[1] # => true
|
19
|
+
Leaf.(2) === Leaf[1] # => false
|
20
|
+
# There are also some shortcuts to use when product has more fields.
|
21
|
+
BTree.() # => BTree.(any, any, any)
|
22
|
+
BTree.(value: any, left: Empty) # => BTree.(any, Empty, any)
|
23
|
+
BTree.(value: any, left: Empty) === BTree[1, Empty, Empty]
|
24
|
+
# => true
|
25
|
+
|
26
|
+
# Any object responding to #=== can be converted to matcher.
|
27
|
+
(1..2).to_m # => Wrapper.(1..2)
|
28
|
+
(1..2).to_m === 2 # => true
|
29
|
+
Empty.to_m # => Empty.to_m
|
30
|
+
# As matchers are using standard #=== method it does not have to be always converted.
|
31
|
+
Empty === Empty # => true
|
32
|
+
Leaf === Leaf[1] # => true
|
33
|
+
|
34
|
+
# Tree matches all its values.
|
35
|
+
[Empty, Leaf[1], Node[Empty, Empty]].all? { |v| Tree === v }
|
36
|
+
# => true
|
37
|
+
|
38
|
+
# There is also a #match method in Matching module to make pattern matching easier.
|
39
|
+
match Leaf[1], # supply the value for matching
|
40
|
+
# if Leaf.(0) matches :zero is returned
|
41
|
+
(on Leaf.(0), :zero),
|
42
|
+
# when computation of the result needs to be avoided use block
|
43
|
+
# if Leaf.(1) matches block is called and its result is returned
|
44
|
+
(on Leaf.(1) do
|
45
|
+
(1..10000).inject(:*) # expensive computation
|
46
|
+
:one # which is :one in this case
|
47
|
+
end) # => :one
|
48
|
+
|
49
|
+
# Alternatively case can be used.
|
50
|
+
case Leaf[1]
|
51
|
+
when Leaf.(0)
|
52
|
+
:zero
|
53
|
+
when Leaf.(1)
|
54
|
+
(1..10000).inject(:*) # expensive computation
|
55
|
+
:one
|
56
|
+
end # => :one
|
57
|
+
|
58
|
+
# But that won't work nicely with value deconstruction.
|
59
|
+
# Each matcher can be marked with #~ method to store value against which is being matched,
|
60
|
+
# each matched value is passed to the block, ...
|
61
|
+
match Leaf[0],
|
62
|
+
(on ~Leaf.(~any) do |leaf, value|
|
63
|
+
[leaf, value]
|
64
|
+
end) # => [Leaf[0], 0]
|
65
|
+
|
66
|
+
btree = BTree[1,
|
67
|
+
BTree[0, Empty, Empty],
|
68
|
+
Empty]
|
69
|
+
# => BTree[value: 1, left: BTree[value: 0, left: Empty, right: Empty], right: Empty]
|
70
|
+
match btree,
|
71
|
+
(on BTree.(any, ~any, ~any) do |left, right|
|
72
|
+
[left, right]
|
73
|
+
end)
|
74
|
+
# => [BTree[value: 0, left: Empty, right: Empty], Empty]
|
75
|
+
|
76
|
+
# or alternatively you can use Ruby's multi-assignment feature.
|
77
|
+
match btree,
|
78
|
+
(on ~BTree do |(_, left, right)|
|
79
|
+
[left, right]
|
80
|
+
end)
|
81
|
+
# => [BTree[value: 0, left: Empty, right: Empty], Empty]
|
82
|
+
|
83
|
+
|
84
|
+
# Matchers also support logical operations #& for and, #| for or, and #! for negation.
|
85
|
+
Color = Algebrick.type do
|
86
|
+
variants Black = atom,
|
87
|
+
White = atom,
|
88
|
+
Pink = atom,
|
89
|
+
Grey = type { fields scale: Float }
|
90
|
+
end # => Color(Black | White | Pink | Grey)
|
91
|
+
|
92
|
+
def color?(color)
|
93
|
+
match color,
|
94
|
+
on(Black | Grey.(-> v { v < 0.2 }), 'black-ish'),
|
95
|
+
on(White | Grey.(-> v { v > 0.8 }), 'white-ish'),
|
96
|
+
on(Grey.(-> v { v >= 0.2 }) & Grey.(-> v { v <= 0.8 }), 'grey-ish'),
|
97
|
+
on(Pink, "that's not a color ;)")
|
98
|
+
end # => :color?
|
99
|
+
|
100
|
+
color? Black # => "black-ish"
|
101
|
+
color? Grey[0.1] # => "black-ish"
|
102
|
+
color? Grey[0.3] # => "grey-ish"
|
103
|
+
color? Grey[0.9] # => "white-ish"
|
104
|
+
color? White # => "white-ish"
|
105
|
+
color? Pink # => "that's not a color ;)"
|
106
|
+
|
107
|
+
# A more complicated example of extracting node's value and values of its left and right side
|
108
|
+
# using also logical operators to allow Empty sides.
|
109
|
+
match BTree[0, Empty, BTree[1, Empty, Empty]],
|
110
|
+
(on BTree.({ value: ~any,
|
111
|
+
left: Empty | BTree.(value: ~any),
|
112
|
+
right: Empty | BTree.(value: ~any) }) do |value, left, right|
|
113
|
+
{ left: left, value: value, right: right }
|
114
|
+
end) # => {:left=>nil, :value=>0, :right=>1}
|
115
|
+
|
116
|
+
# There is also a more funky syntax for matching
|
117
|
+
# using #>, #>> and Ruby 1.9 syntax for lambdas `-> {}`.
|
118
|
+
match Leaf[1],
|
119
|
+
Leaf.(0) >> :zero,
|
120
|
+
Leaf.(~any) >-> value do
|
121
|
+
(1..value).inject(:*) # an expensive computation
|
122
|
+
end # => 1
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Let's define a Tree
|
2
|
+
Tree = Algebrick.type do |tree|
|
3
|
+
variants Empty = atom,
|
4
|
+
Leaf = type { fields Integer },
|
5
|
+
Node = type { fields tree, tree }
|
6
|
+
end
|
7
|
+
|
8
|
+
# Now types `Tree(Empty | Leaf | Node)`, `Empty`, `Leaf(Integer)` and `Node(Tree, Tree)` are defined.
|
9
|
+
# Let's add a method, don't miss the **pattern matching** example.
|
10
|
+
module Tree
|
11
|
+
# compute depth of a tree
|
12
|
+
def depth
|
13
|
+
match self,
|
14
|
+
(on Empty, 0),
|
15
|
+
(on Leaf, 1),
|
16
|
+
# ~ will store and pass matched parts to variables left and right
|
17
|
+
(on Node.(~any, ~any) do |left, right|
|
18
|
+
1 + [left.depth, right.depth].max
|
19
|
+
end)
|
20
|
+
end
|
21
|
+
end #
|
22
|
+
|
23
|
+
# Method defined in module `Tree` are passed down to **all** values of type Tree.
|
24
|
+
Empty.depth
|
25
|
+
Leaf[10].depth
|
26
|
+
Node[Leaf[4], Empty].depth
|
27
|
+
Node[Empty, Node[Leaf[1], Empty]].depth
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Let's define a Tree
|
2
|
+
Tree = Algebrick.type do |tree|
|
3
|
+
variants Empty = atom,
|
4
|
+
Leaf = type { fields Integer },
|
5
|
+
Node = type { fields tree, tree }
|
6
|
+
end # => Tree(Empty | Leaf | Node)
|
7
|
+
|
8
|
+
# Now types `Tree(Empty | Leaf | Node)`, `Empty`, `Leaf(Integer)` and `Node(Tree, Tree)` are defined.
|
9
|
+
# Let's add a method, don't miss the **pattern matching** example.
|
10
|
+
module Tree
|
11
|
+
# compute depth of a tree
|
12
|
+
def depth
|
13
|
+
match self,
|
14
|
+
(on Empty, 0),
|
15
|
+
(on Leaf, 1),
|
16
|
+
# ~ will store and pass matched parts to variables left and right
|
17
|
+
(on Node.(~any, ~any) do |left, right|
|
18
|
+
1 + [left.depth, right.depth].max
|
19
|
+
end)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Method defined in module `Tree` are passed down to **all** values of type Tree.
|
24
|
+
Empty.depth # => 0
|
25
|
+
Leaf[10].depth # => 1
|
26
|
+
Node[Leaf[4], Empty].depth # => 2
|
27
|
+
Node[Empty, Node[Leaf[1], Empty]].depth # => 3
|
data/doc/tree1.in.rb
ADDED
data/doc/tree1.out.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Tree = Algebrick.type do |tree|
|
2
|
+
variants Empty = type,
|
3
|
+
Leaf = type { fields Integer },
|
4
|
+
Node = type { fields tree, tree }
|
5
|
+
end # => Tree(Empty | Leaf | Node)
|
6
|
+
# Which sets 4 modules representing these types in current module/class
|
7
|
+
Tree # => Tree(Empty | Leaf | Node)
|
8
|
+
Empty # => Empty
|
9
|
+
Leaf # => Leaf(Integer)
|
10
|
+
Node # => Node(Tree, Tree)
|
data/doc/type_def.in.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Let's define some types
|
2
|
+
Maybe = Algebrick.type do
|
3
|
+
variants None = atom,
|
4
|
+
Some = type { fields Numeric }
|
5
|
+
end
|
6
|
+
|
7
|
+
# where the Maybe actually is:
|
8
|
+
Maybe.class
|
9
|
+
Maybe.class.superclass
|
10
|
+
Maybe.class.superclass.superclass
|
11
|
+
Maybe.class.superclass.superclass.superclass
|
12
|
+
|
13
|
+
# if there is a circular dependency you can define the dependent types inside the block like this:
|
14
|
+
Tree = Algebrick.type do |tree|
|
15
|
+
variants Empty = type,
|
16
|
+
Leaf = type { fields Integer },
|
17
|
+
Node = type { fields tree, tree }
|
18
|
+
end
|
19
|
+
Empty
|
20
|
+
Leaf
|
21
|
+
Node
|
data/doc/type_def.out.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Let's define some types
|
2
|
+
Maybe = Algebrick.type do
|
3
|
+
variants None = atom,
|
4
|
+
Some = type { fields Numeric }
|
5
|
+
end # => Maybe(None | Some)
|
6
|
+
|
7
|
+
# where the Maybe actually is:
|
8
|
+
Maybe.class # => Algebrick::ProductVariant
|
9
|
+
Maybe.class.superclass # => Algebrick::Type
|
10
|
+
Maybe.class.superclass.superclass # => Module
|
11
|
+
Maybe.class.superclass.superclass.superclass # => Object
|
12
|
+
|
13
|
+
# if there is a circular dependency you can define the dependent types inside the block like this:
|
14
|
+
Tree = Algebrick.type do |tree|
|
15
|
+
variants Empty = type,
|
16
|
+
Leaf = type { fields Integer },
|
17
|
+
Node = type { fields tree, tree }
|
18
|
+
end # => Tree(Empty | Leaf | Node)
|
19
|
+
Empty # => Empty
|
20
|
+
Leaf # => Leaf(Integer)
|
21
|
+
Node # => Node(Tree, Tree)
|
data/doc/values.in.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Let's define a Tree
|
2
|
+
Tree = Algebrick.type do |tree|
|
3
|
+
variants Empty = type,
|
4
|
+
Leaf = type { fields Integer },
|
5
|
+
Node = type { fields tree, tree }
|
6
|
+
end
|
7
|
+
|
8
|
+
# Values of atomic types are represented by itself,
|
9
|
+
# they are theirs own value.
|
10
|
+
Empty.kind_of? Algebrick::Value
|
11
|
+
Empty.kind_of? Algebrick::Type
|
12
|
+
Empty.kind_of? Empty
|
13
|
+
|
14
|
+
# Values of product types are constructed with #[] and they are immutable.
|
15
|
+
Leaf[1].kind_of? Algebrick::Value
|
16
|
+
Leaf[1].kind_of? Leaf
|
17
|
+
Node[Empty, Empty].kind_of? Node
|
18
|
+
|
19
|
+
# Variant does not have its own values, it uses atoms and products.
|
20
|
+
Leaf[1].kind_of? Tree
|
21
|
+
Empty.kind_of? Tree
|
22
|
+
|
23
|
+
# Product can have its fields named.
|
24
|
+
BinaryTree = BTree = Algebrick.type do |btree|
|
25
|
+
fields! value: Integer, left: btree, right: btree
|
26
|
+
variants Tip = atom, btree
|
27
|
+
end
|
28
|
+
|
29
|
+
# Then values can be created with names
|
30
|
+
tree1 = BTree[value: 1, left: Tip, right: Tip]
|
31
|
+
# or without them as before.
|
32
|
+
BTree[0, Tip, tree1]
|
33
|
+
|
34
|
+
# Fields of products can be read as follows:
|
35
|
+
# 1. When type has only one field method #value is defined
|
36
|
+
Leaf[1].value
|
37
|
+
# 2. By multi-assign
|
38
|
+
v, left, right = BTree[value: 1, left: Tip, right: Tip]
|
39
|
+
[v, left, right]
|
40
|
+
# 3. With #[] method when fields are named
|
41
|
+
BTree[value: 1, left: Tip, right: Tip][:value]
|
42
|
+
BTree[value: 1, left: Tip, right: Tip][:left]
|
43
|
+
# 4. With methods named by fields when fields are named
|
44
|
+
# (it can be disabled if fields are defined with #fields instead of #fields!)
|
45
|
+
BTree[1, Tip, Tip].value
|
46
|
+
BTree[1, Tip, Tip].left
|
47
|
+
|
48
|
+
# Product instantiation raises TypeError when being constructed with wrong type.
|
49
|
+
Leaf['a'] rescue $!
|
50
|
+
Node[nil, Empty] rescue $!
|
51
|
+
|
52
|
+
|
data/doc/values.out.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Let's define a Tree
|
2
|
+
Tree = Algebrick.type do |tree|
|
3
|
+
variants Empty = type,
|
4
|
+
Leaf = type { fields Integer },
|
5
|
+
Node = type { fields tree, tree }
|
6
|
+
end # => Tree(Empty | Leaf | Node)
|
7
|
+
|
8
|
+
# Values of atomic types are represented by itself,
|
9
|
+
# they are theirs own value.
|
10
|
+
Empty.kind_of? Algebrick::Value # => true
|
11
|
+
Empty.kind_of? Algebrick::Type # => true
|
12
|
+
Empty.kind_of? Empty # => true
|
13
|
+
|
14
|
+
# Values of product types are constructed with #[] and they are immutable.
|
15
|
+
Leaf[1].kind_of? Algebrick::Value # => true
|
16
|
+
Leaf[1].kind_of? Leaf # => true
|
17
|
+
Node[Empty, Empty].kind_of? Node # => true
|
18
|
+
|
19
|
+
# Variant does not have its own values, it uses atoms and products.
|
20
|
+
Leaf[1].kind_of? Tree # => true
|
21
|
+
Empty.kind_of? Tree # => true
|
22
|
+
|
23
|
+
# Product can have its fields named.
|
24
|
+
BinaryTree = BTree = Algebrick.type do |btree|
|
25
|
+
fields! value: Integer, left: btree, right: btree
|
26
|
+
variants Tip = atom, btree
|
27
|
+
end
|
28
|
+
# => BTree(Tip | BTree(value: Integer, left: BTree, right: BTree))
|
29
|
+
|
30
|
+
# Then values can be created with names
|
31
|
+
tree1 = BTree[value: 1, left: Tip, right: Tip]
|
32
|
+
# => BTree[value: 1, left: Tip, right: Tip]
|
33
|
+
# or without them as before.
|
34
|
+
BTree[0, Tip, tree1]
|
35
|
+
# => BTree[value: 0, left: Tip, right: BTree[value: 1, left: Tip, right: Tip]]
|
36
|
+
|
37
|
+
# Fields of products can be read as follows:
|
38
|
+
# 1. When type has only one field method #value is defined
|
39
|
+
Leaf[1].value # => 1
|
40
|
+
# 2. By multi-assign
|
41
|
+
v, left, right = BTree[value: 1, left: Tip, right: Tip]
|
42
|
+
# => BTree[value: 1, left: Tip, right: Tip]
|
43
|
+
[v, left, right] # => [1, Tip, Tip]
|
44
|
+
# 3. With #[] method when fields are named
|
45
|
+
BTree[value: 1, left: Tip, right: Tip][:value] # => 1
|
46
|
+
BTree[value: 1, left: Tip, right: Tip][:left] # => Tip
|
47
|
+
# 4. With methods named by fields when fields are named
|
48
|
+
# (it can be disabled if fields are defined with #fields instead of #fields!)
|
49
|
+
BTree[1, Tip, Tip].value # => 1
|
50
|
+
BTree[1, Tip, Tip].left # => Tip
|
51
|
+
|
52
|
+
# Product instantiation raises TypeError when being constructed with wrong type.
|
53
|
+
Leaf['a'] rescue $!
|
54
|
+
# => #<TypeError: Value (String) 'a' is not any of: Integer.>
|
55
|
+
Node[nil, Empty] rescue $!
|
56
|
+
# => #<TypeError: Value (NilClass) '' is not any of: Tree(Empty | Leaf | Node).>
|
57
|
+
|
58
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Copyright 2013 Petr Chalupa <git+algebrick@pitr.ch>
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Algebrick
|
16
|
+
# Representation of Atomic types
|
17
|
+
class Atom < Type
|
18
|
+
include Value
|
19
|
+
|
20
|
+
def initialize(name, &block)
|
21
|
+
super name, &block
|
22
|
+
extend self
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_m
|
26
|
+
Matchers::Atom.new self
|
27
|
+
end
|
28
|
+
|
29
|
+
def be_kind_of(type)
|
30
|
+
extend type
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
self.equal? other
|
35
|
+
end
|
36
|
+
|
37
|
+
def type
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
name
|
43
|
+
end
|
44
|
+
|
45
|
+
def pretty_print(q)
|
46
|
+
q.text to_s
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|