mon 0.0.2

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.
@@ -0,0 +1,89 @@
1
+ # The List monad wraps lists. Gasp!
2
+ #
3
+ # A simple example:
4
+ # <tt>l = List[1, 2, 3]
5
+ # list9 = l.bind { |i| i * 9 } # ==> List[9, 18, 27]
6
+ # longList = l.bind { |i| [i, i * 2] } # ==> List[1, 2, 2, 4, 3, 6]</tt>
7
+ #
8
+ # Simple and straightforward.
9
+
10
+ module Mon
11
+
12
+ module Monad
13
+
14
+ require_relative 'chainable_monad'
15
+ require_relative 'monad'
16
+
17
+ class List < Monad
18
+
19
+ include ChainableMonad
20
+
21
+ def initialize(list)
22
+ @list = list
23
+ end
24
+
25
+ # Create a list. Eg: List[1, 2, 3]
26
+ def self.[](*list)
27
+ List.new(list)
28
+ end
29
+
30
+ # Apply fun to all elements of the list (ala map):
31
+ # List[1, 2, 3].map { |i| i + 5 } # ==> List[6, 7, 8]
32
+ def bind &fun
33
+ List.send(:new, @list.map { |i| fun.call(i) }.map { |i| (i.is_a? List) ? i.to_a : i}.flatten(1))
34
+ end
35
+
36
+ # Return the array wrapped by the List
37
+ def unwrap
38
+ to_a
39
+ end
40
+
41
+ # Alias for #unwrap
42
+ def _
43
+ to_a
44
+ end
45
+
46
+ def _canBind? name
47
+ @list.all? { |i| i.respond_to?(name) }
48
+ end
49
+
50
+ def to_s
51
+ (@list.length > 3) ? (tail = "...") : tail = ""
52
+ "List[#{ @list.take(3).join(", ") }#{ tail }]"
53
+ end
54
+
55
+ # Return the array wrapped by the list
56
+ def to_a
57
+ @list
58
+ end
59
+
60
+ class << self
61
+ protected :new
62
+ end
63
+
64
+ def eql?(other)
65
+ if other.is_a? List
66
+ @list == other.unwrap
67
+ elsif other.is_a? Array
68
+ @list == other
69
+ else
70
+ false
71
+ end
72
+ end
73
+
74
+ def ==(o)
75
+ eql?(o)
76
+ end
77
+
78
+ def equal?(o)
79
+ eql?(o)
80
+ end
81
+
82
+ def self::valid?(o)
83
+ o.is_a?(List)
84
+ end
85
+
86
+ protected :unwrap, :_canBind?
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,211 @@
1
+ # The Maybe monad wraps a possible value. It allows chaining of
2
+ # operations on a potentially-null value.
3
+ #
4
+ # A simple example:
5
+ # <tt>m = Maybe[callSomeIntOrNilFunction()]
6
+ # v = (m * 3 + 2).abs
7
+ # # Depending whether the original method call returned a number or nil,
8
+ # # could be either None or Some[N].</tt>
9
+
10
+ module Mon
11
+
12
+ module Monad
13
+
14
+ require_relative 'chainable_monad'
15
+ require_relative 'monad'
16
+
17
+ # Superclass for Some and None. Can be used as follows:
18
+ # <tt>m = Maybe[nil] # ==> None
19
+ # m = Maybe[5] # ==> Some[5]
20
+ # m = Maybe[nil] * 7 # ==> None
21
+ # m = Maybe[5] * 7 # ==> Some[35]
22
+ # m = Maybe[call_to_fun].someOperation(3) # ==> Some[...] or None, never an error</tt>
23
+ class Maybe < Monad
24
+ include ChainableMonad
25
+
26
+ # Override to catch None
27
+ def method_missing(name, *args, &fun)
28
+ self.bind { |o| o.send(name, *args, &fun) }
29
+ end
30
+
31
+ # Use to instantiate a Maybe monad:
32
+ # <tt>m = Maybe[<either nil/false or not>]</tt>
33
+ def self.[](obj)
34
+ if obj.is_a? Maybe
35
+ obj
36
+ elsif obj
37
+ Some[obj]
38
+ else
39
+ None[]
40
+ end
41
+ end
42
+
43
+ # Get the value, or throw an exception (using the optional supplied msg) if it's empty
44
+ def orFail(msg = nil)
45
+ msg = "#{ self } is empty!" unless msg
46
+ throw RuntimeError.new(msg)
47
+ end
48
+
49
+ # For Contracts, DEPRECATED
50
+ def valid?(o)
51
+ o.is_a? Maybe
52
+ end
53
+
54
+ class << self
55
+ protected :new
56
+ end
57
+ end
58
+
59
+ # The Some class represents a value that is NOT null/false
60
+ class Some < Maybe
61
+
62
+ def initialize(obj)
63
+ @obj = obj
64
+ super()
65
+ end
66
+
67
+ # Wrap an object. You probably want Maybe[...], but there's a slight difference:
68
+ # Maybe[nil] # ==> None
69
+ # Some[nil] # ==> Some[nil]
70
+ def self.[](obj)
71
+ if obj.is_a? None
72
+ obj
73
+ else
74
+ Some.new(obj)
75
+ end
76
+ end
77
+
78
+ # Unwrap the wrapped value, nil or not
79
+ def unwrap
80
+ @obj
81
+ end
82
+
83
+ def _canBind? name
84
+ @obj.respond_to? name
85
+ end
86
+
87
+ # If there's a wrapped value, return it. Otherwise, either return
88
+ # the supplied object, or execute the supplied block.
89
+ # Eg:
90
+ # <tt>Maybe[1].or(5) # ==> 1
91
+ # Maybe[nil].or(5) # ==> 5
92
+ # Maybe[nil].or { throw Exception.new("...") } # ==> Exception!
93
+ def or obj = nil, &f
94
+ @obj
95
+ end
96
+
97
+ # Get the value, or throw an exception (using the optional supplied msg) if it's empty
98
+ def orFail(msg = nil)
99
+ msg = "No such value!" if msg.nil?
100
+ self::or { throw RuntimeError.new(msg) }
101
+ end
102
+
103
+ # If we're wrapping a value, apply fun() to it. Otherwise, None stays None.
104
+ def bind &fun
105
+ o = fun.call(@obj)
106
+ if o.is_a? Maybe
107
+ o
108
+ elsif o
109
+ Some[o]
110
+ else
111
+ None::none
112
+ end
113
+ end
114
+
115
+ def to_s
116
+ "Some[#{ @obj.to_s }]"
117
+ end
118
+
119
+ def eql?(other)
120
+ if other.is_a? Some
121
+ @obj == other.unwrap
122
+ else
123
+ @obj == other
124
+ end
125
+ end
126
+
127
+ def ==(o)
128
+ eql?(o)
129
+ end
130
+
131
+ def equal?(o)
132
+ eql?(o)
133
+ end
134
+
135
+ class << self
136
+ protected :new
137
+ end
138
+
139
+ protected :unwrap, :_canBind?
140
+ end
141
+
142
+ class None < Maybe
143
+
144
+ # If we're wrapping a value, apply fun() to it. Otherwise, None stays None.
145
+ def bind &fun
146
+ self
147
+ end
148
+
149
+ # Unwrap the wrapped value, nil or not
150
+ def unwrap
151
+ nil
152
+ end
153
+
154
+ # If there's a wrapped value, return it. Otherwise, either return
155
+ # the supplied object, or execute the supplied block.
156
+ # Eg:
157
+ # <tt>Maybe[1].or(5) # ==> 1
158
+ # Maybe[nil].or(5) # ==> 5
159
+ # Maybe[nil].or { throw Exception.new("...") } # ==> Exception!</tt>
160
+ def or obj = nil, &f
161
+ if obj and f
162
+ f.call obj
163
+ elsif f
164
+ f.call
165
+ else
166
+ obj
167
+ end
168
+ end
169
+
170
+ def _canBind? name
171
+ true
172
+ end
173
+
174
+ # Return a new None object. Takes no args.
175
+ # <tt>None[] # ==> None</tt>
176
+ def self.[]
177
+ None.new
178
+ end
179
+
180
+ def to_s
181
+ "None"
182
+ end
183
+
184
+ def initialize
185
+ super()
186
+ end
187
+
188
+ def eql?(other)
189
+ other.is_a? None
190
+ end
191
+
192
+ def ==(o)
193
+ eql?(o)
194
+ end
195
+
196
+ def equal?(o)
197
+ eql?(o)
198
+ end
199
+
200
+ def self::none
201
+ self.new
202
+ end
203
+
204
+ class << self
205
+ protected :new
206
+ end
207
+
208
+ protected :unwrap, :_canBind?
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,11 @@
1
+
2
+ module Mon
3
+ module Monad
4
+
5
+ class Monad
6
+ # Empty class, for matching purposes
7
+ private_class_method :new
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,175 @@
1
+ # The React monad wraps a changeable value, and catches changes to that value.
2
+ #
3
+ # A simple example:
4
+ # <tt>r = React[5]
5
+ # d = r * r
6
+ # # Currently r = Reactron[5], d = Reactor[25]
7
+ # r << 25
8
+ # # Now r = Reactron[25], d = Reactor[625]
9
+ # dd = d * 2
10
+ # # d = Reactor[625], dd = Reactor[1250]</tt>
11
+ #
12
+ # More or less, this is a Listener pattern. Reactrons are listenable,
13
+ # Reactors are listeners.
14
+
15
+ module Mon
16
+
17
+ module Monad
18
+
19
+ require_relative 'chainable_monad'
20
+ require_relative 'monad'
21
+
22
+ # The React class is the parent of Reactron and Reactor. The key difference
23
+ # between the two is the presence of the << operator. The value of a Reactor is
24
+ # derived from a Reactron, and cannot be directly changed. If viewed as a tree,
25
+ # Reactrons are the root, and only the root may be changed.
26
+ #
27
+ # However, we can have more than one root!
28
+ # <tt>m = React[2] # ==> Reactron[2]
29
+ # n = React[10] # ==> Reactron[10]
30
+ # v = m * n # Currently v == Reactor[20]
31
+ # n << 5 # Now v == Reactor[10]</tt>
32
+ #
33
+ # Usage of React is straightforward:
34
+ # <tt>r = React[some_value]</tt>
35
+ class React < Monad
36
+ include ChainableMonad
37
+
38
+ # Wrap a value in a Reactron:
39
+ # <tt>r = React["test"] # ==> Reactron["test"]
40
+ def self::[] obj
41
+ if (obj.is_a? Proc)
42
+ Reactor[obj]
43
+ else
44
+ Reactron[obj]
45
+ end
46
+ end
47
+
48
+ class << self
49
+ protected :new
50
+ end
51
+
52
+ def self::valid?(v)
53
+ v.is_a? React
54
+ end
55
+ end
56
+
57
+ # The Reactron class represents a changeable value, from which other
58
+ # values can be derived (as Reactrons)
59
+ class Reactron < React
60
+
61
+ def initialize(obj)
62
+ @obj = obj
63
+ end
64
+
65
+ # You should be using React[...]
66
+ def self::[] obj
67
+ Reactron.new(obj)
68
+ end
69
+
70
+ # Apply fun to the value wrapped by this Reactron. Returns a
71
+ # Reactor. Whenever the value is changed (with <<), all derived
72
+ # Reactors are also updated.
73
+ def bind &fun
74
+ Reactor[lambda { fun.call(self.unwrap) }]
75
+ end
76
+
77
+ def _canBind? name
78
+ @obj.respond_to? name
79
+ end
80
+
81
+ # Unwrap the value contained by this Reactron
82
+ def unwrap
83
+ @obj
84
+ end
85
+
86
+ # Change the value of this Reactron
87
+ def << obj
88
+ @obj = obj
89
+ self
90
+ end
91
+
92
+ def eql? o
93
+ if o.is_a? React
94
+ @obj == o.unwrap
95
+ else
96
+ @obj == o
97
+ end
98
+ end
99
+
100
+ def equal? o
101
+ eql? o
102
+ end
103
+
104
+ def ==(o)
105
+ eql? o
106
+ end
107
+
108
+ def to_s
109
+ "Reactron[#{ @obj }]"
110
+ end
111
+
112
+ class << self
113
+ protected :new
114
+ end
115
+
116
+ protected :_canBind?
117
+ end
118
+
119
+ class Reactor < React
120
+
121
+ def initialize(fun)
122
+ @fun = fun
123
+ end
124
+
125
+ # Apply fun to the value wrapped by this Reactor (which in turn is
126
+ # a transform on some Reactron), returning another
127
+ # Reactron. Changes to the root Reactron will propagate through
128
+ # the whole tree.
129
+ def bind &fun
130
+ Reactor[lambda { fun.call(self.unwrap) }]
131
+ end
132
+
133
+ # You want React[...]
134
+ def self::[] fun
135
+ Reactor.new(fun)
136
+ end
137
+
138
+ # Unwrap the (current) value contained by this Reactor
139
+ def unwrap
140
+ r = @fun.call
141
+ r.is_a?(Reactor) ? r.unwrap : r
142
+ end
143
+
144
+ def _canBind? name
145
+ unwrap.respond_to? name
146
+ end
147
+
148
+ def eql? o
149
+ if o.is_a? React
150
+ @fun.call == o.unwrap
151
+ else
152
+ @fun.call == o
153
+ end
154
+ end
155
+
156
+ def equal? o
157
+ eql? o
158
+ end
159
+
160
+ def ==(o)
161
+ eql? o
162
+ end
163
+
164
+ def to_s
165
+ "Reactor[#{ unwrap }]"
166
+ end
167
+
168
+ class << self
169
+ protected :new
170
+ end
171
+
172
+ protected :_canBind?
173
+ end
174
+ end
175
+ end