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.
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