algebrick 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -3
  3. data/README_FULL.md +13 -21
  4. data/VERSION +1 -1
  5. data/doc/actor.rb +21 -0
  6. data/doc/data.in.rb +99 -0
  7. data/doc/data.out.rb +103 -0
  8. data/doc/extending_behavior.in.rb +61 -0
  9. data/doc/extending_behavior.out.rb +62 -0
  10. data/doc/format.rb +75 -0
  11. data/doc/init.rb +1 -0
  12. data/doc/json.in.rb +39 -0
  13. data/doc/json.out.rb +43 -0
  14. data/doc/null.in.rb +36 -0
  15. data/doc/null.out.rb +40 -0
  16. data/doc/parametrized.in.rb +37 -0
  17. data/doc/parametrized.out.rb +41 -0
  18. data/doc/pattern_matching.in.rb +116 -0
  19. data/doc/pattern_matching.out.rb +122 -0
  20. data/doc/quick_example.in.rb +27 -0
  21. data/doc/quick_example.out.rb +27 -0
  22. data/doc/tree1.in.rb +10 -0
  23. data/doc/tree1.out.rb +10 -0
  24. data/doc/type_def.in.rb +21 -0
  25. data/doc/type_def.out.rb +21 -0
  26. data/doc/values.in.rb +52 -0
  27. data/doc/values.out.rb +58 -0
  28. data/lib/algebrick/atom.rb +49 -0
  29. data/lib/algebrick/dsl.rb +104 -0
  30. data/lib/algebrick/field_method_readers.rb +43 -0
  31. data/lib/algebrick/matcher_delegations.rb +45 -0
  32. data/lib/algebrick/matchers/abstract.rb +127 -0
  33. data/lib/algebrick/matchers/abstract_logic.rb +38 -0
  34. data/lib/algebrick/matchers/and.rb +29 -0
  35. data/lib/algebrick/matchers/any.rb +37 -0
  36. data/lib/algebrick/matchers/array.rb +57 -0
  37. data/lib/algebrick/matchers/atom.rb +28 -0
  38. data/lib/algebrick/matchers/not.rb +44 -0
  39. data/lib/algebrick/matchers/or.rb +51 -0
  40. data/lib/algebrick/matchers/product.rb +73 -0
  41. data/lib/algebrick/matchers/variant.rb +29 -0
  42. data/lib/algebrick/matchers/wrapper.rb +57 -0
  43. data/lib/algebrick/matchers.rb +31 -0
  44. data/lib/algebrick/matching.rb +62 -0
  45. data/lib/algebrick/parametrized_type.rb +122 -0
  46. data/lib/algebrick/product_constructors/abstract.rb +70 -0
  47. data/lib/algebrick/product_constructors/basic.rb +47 -0
  48. data/lib/algebrick/product_constructors/named.rb +58 -0
  49. data/lib/algebrick/product_constructors.rb +25 -0
  50. data/lib/algebrick/product_variant.rb +195 -0
  51. data/lib/algebrick/reclude.rb +39 -0
  52. data/lib/algebrick/serializer.rb +129 -0
  53. data/lib/algebrick/serializers.rb +25 -0
  54. data/lib/algebrick/type.rb +61 -0
  55. data/lib/algebrick/type_check.rb +58 -0
  56. data/lib/algebrick/types.rb +59 -0
  57. data/lib/algebrick/value.rb +41 -0
  58. data/lib/algebrick.rb +14 -1170
  59. data/spec/algebrick_test.rb +708 -0
  60. 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
@@ -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
6
+ # Which sets 4 modules representing these types in current module/class
7
+ Tree
8
+ Empty
9
+ Leaf
10
+ Node
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)
@@ -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
@@ -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