algebrick 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|