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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a7a736fe71441c3d09d26400d69898a67e4188ca
4
+ data.tar.gz: d68172fc1348c0d680e9f04f9dd4eeb4369f4f83
5
+ SHA512:
6
+ metadata.gz: bc9996b5de285ab1f70da5cf4b5ea8ccdb57f917ae1318d02135a050798662b7360fe0abdec5b640e4f4de0b9756d9a08b4fa9c3a72843fb0792d2ed07f45ead
7
+ data.tar.gz: 647fb1982589a44e407a2d8c4e890d28c8d12ac3eafc7b5ee22acf09e6096ebcd6081ce2ef4a3accebd3af1c698b5b0bc0281f825cadd053490b287c1f5e3451
@@ -0,0 +1,12 @@
1
+
2
+ class TrueClass
3
+ def implies(o)
4
+ o ? true : false
5
+ end
6
+ end
7
+
8
+ class FalseClass
9
+ def implies(o)
10
+ true
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ # Contract for the Mon::Future monad, for use with the Contracts gem
2
+ # For example:
3
+ # <tt>Contract C::Future[Num] => C::Future[Num]
4
+ # def futureSquare(l)
5
+ # l.bind { |i| i * i }
6
+ # end</tt>
7
+ # will work only for (eg) futureSquare(Mon::Future.eventually { wait_for_number_from_thread }).
8
+ # If passed anything (or returning anything) other than a Mon::Future, a
9
+ # ContractViolation will be thrown. <b>Note:</b> since Ruby is dynamically typed,
10
+ # we can't ensure that the eventual value will be of the correct type. So:
11
+ # futureSquare(Mon::Future.eventually { wait_for_string_from_thread }) is likely
12
+ # to succeed (if the thread is still in-flight), and fail when the thread returns.
13
+ # Completed Futures don't have this issue.
14
+
15
+ module Mon
16
+ module Contract
17
+ require_relative 'monad_contract'
18
+
19
+ class Future < MonadContract
20
+ def valid?(val)
21
+ # Should be a Future, and if finalized it should satisfy
22
+ # the provided contract. Note: We can't know if an inflight
23
+ # contract will satisfy type or not.
24
+ val.is_a?(Mon::M::Future) and (val.is_a?(Mon::M::FutureComplete).implies(valid_nested_contract?(val._)))
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # Contract for the Mon::Lazy monad, for use with the Contracts gem
2
+ # For example:
3
+ # <tt>Contract C::Lazy[Num] => C::Lazy[Num]
4
+ # def lazySquare(l)
5
+ # l.bind { |i| i * i }
6
+ # end</tt>
7
+ # will work only for (eg) lazySquare(Mon::Lazy.eventually { perform_painful_op_only_if_necessary() }).
8
+ # If passed anything (or returning anything) other than a Mon::Lazy, a
9
+ # ContractViolation will be thrown. <b>Note:</b> since Ruby is dynamically typed,
10
+ # we can't ensure that the eventual value will be of the correct type. So:
11
+ # lazySquare(Mon::Lazy.eventually { op_returning_string }) is likely
12
+ # to succeed, and fail when the operation is actually executed.
13
+ # Finalized Lazy monads don't have this issue.
14
+
15
+ module Mon
16
+ module Contract
17
+ require_relative 'monad_contract'
18
+
19
+ class Lazy < MonadContract
20
+ def valid?(val)
21
+ # Should be a Lazy (i.e. pending or final), and if it's
22
+ # final, the value should be valid re: the provided contract
23
+ val.is_a?(Mon::M::Lazy) and (val.is_a?(Mon::M::Final).implies(valid_nested_contract?(val._)))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ # Contract for the Mon::List monad, for use with the Contracts gem
2
+ # For example:
3
+ # <tt>Contract C::List[Num] => C::List[Num]
4
+ # def listSquare(l)
5
+ # l.bind { |i| i * i }
6
+ # end</tt>
7
+ # will work only for listSquare(Mon::List[1, 2, 3, 4]). If passed
8
+ # anything (or returning anything) other than a Mon::List, a
9
+ # ContractViolation will be thrown.
10
+
11
+ module Mon
12
+ module Contract
13
+ require_relative 'monad_contract'
14
+
15
+ class List < MonadContract
16
+ def valid?(val)
17
+ # Should be a List whose elements satisfy the given contract
18
+ val.is_a?(Mon::M::List) and (val._.all? { |el| valid_nested_contract?(el) })
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+
2
+ module Mon
3
+ module Contract
4
+ require_relative 'monad_contract'
5
+
6
+ class Maybe < MonadContract
7
+
8
+ def valid?(val)
9
+ # Should either be None or valid?
10
+ val.is_a?(Mon::M::Maybe) and (val.is_a?(Mon::M::Some).implies(valid_nested_contract?(val._)))
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,57 @@
1
+
2
+ module Mon
3
+ module Contract
4
+ require_relative '../mon.rb'
5
+ require 'contracts'
6
+
7
+ class MonadContract < Contracts::CallableClass
8
+ require_relative 'contract_helpers'
9
+
10
+ def initialize(*vals)
11
+ if vals.length != 1
12
+ throw ArgumentError.new("Incorrect usage of #{ self.class.name } contract, should be #{ self.class.name }[<contract>]")
13
+ end
14
+ @nested_contract = vals[0]
15
+ end
16
+
17
+ def nested_contract
18
+ @nested_contract
19
+ end
20
+
21
+ def valid?(val)
22
+ throw RuntimeError.new("MonadContract is abstract, #valid? must be overridden!")
23
+ end
24
+
25
+ def valid_nested_contract?(val)
26
+ nested_validator = Object::Contract::make_validator(@nested_contract)
27
+ nested_validator.call(val)
28
+ end
29
+
30
+ def to_s
31
+ "#{ self.class.name }[#{@nested_contract.to_s}]"
32
+ end
33
+ end
34
+
35
+ class Monad < MonadContract
36
+ def valid?(val)
37
+ return false unless (val.is_a? Mon::M::Monad)
38
+ case val
39
+ when Mon::M::List
40
+ Mon::C::List.new(@nested_contract).valid?(val)
41
+ when Mon::M::Maybe
42
+ Mon::C::Maybe.new(@nested_contract).valid?(val)
43
+ when Mon::M::Try
44
+ Mon::C::Try.new(@nested_contract).valid?(val)
45
+ when Mon::M::Lazy
46
+ Mon::C::Lazy.new(@nested_contract).valid?(val)
47
+ when Mon::M::Future
48
+ Mon::C::Future.new(@nested_contract).valid?(val)
49
+ when Mon::M::React
50
+ Mon::C::React.new(@nested_contract).valid?(val)
51
+ else
52
+ raise RuntimeError.new("Unrecognized monad: #{ val.class }!")
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,23 @@
1
+ # Contract for the Mon::React monad, for use with the Contracts gem
2
+ # For example:
3
+ # <tt>Contract C::React[Num] => C::React[Num]
4
+ # def reactSquare(l)
5
+ # l.bind { |i| i * i }
6
+ # end</tt>
7
+ # will work only for (eg) reactSquare(Mon::React[3]).
8
+ # If passed anything (or returning anything) other than a Mon::React, a
9
+ # ContractViolation will be thrown.
10
+
11
+ module Mon
12
+ module Contract
13
+ require_relative 'monad_contract'
14
+
15
+ class React < MonadContract
16
+ def valid?(val)
17
+ # Should be a React (i.e. success or failure), and if it's a
18
+ # success, the value should be valid re: the provided contract
19
+ val.is_a?(Mon::M::React) and (valid_nested_contract?(val._))
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # Contract for the Mon::Try monad, for use with the Contracts gem
2
+ # For example:
3
+ # <tt>Contract C::Try[Num] => C::Try[Num]
4
+ # def trySquare(l)
5
+ # l.bind { |i| i * i }
6
+ # end</tt>
7
+ # will work only for (eg) trySquare(Mon::Try.to { get_num_from_remote_service() }). If passed
8
+ # anything (or returning anything) other than a Mon::Try, a
9
+ # ContractViolation will be thrown.
10
+
11
+ module Mon
12
+ module Contract
13
+ require_relative 'monad_contract'
14
+
15
+ class Try < MonadContract
16
+ def valid?(val)
17
+ # Should be a Try (i.e. success or failure), and if it's a
18
+ # success, the value should be valid re: the provided contract
19
+ val.is_a?(Mon::M::Try) and (val.is_a?(Mon::M::Success).implies(valid_nested_contract?(val._)))
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module Mon
3
+ require_relative 'monads/list'
4
+ require_relative 'monads/try'
5
+ require_relative 'monads/maybe'
6
+ require_relative 'monads/lazy'
7
+ require_relative 'monads/future'
8
+ require_relative 'monads/reactron'
9
+
10
+ require_relative 'contracts/list'
11
+ require_relative 'contracts/try'
12
+ require_relative 'contracts/maybe'
13
+ require_relative 'contracts/lazy'
14
+ require_relative 'contracts/future'
15
+ require_relative 'contracts/reactron'
16
+ require_relative 'contracts/monad_contract'
17
+
18
+ # Namespace aliases
19
+ C = Contract
20
+ M = Monad
21
+ end
@@ -0,0 +1,23 @@
1
+ # Base class for Mon monads. Do not instantiate.
2
+
3
+ module Mon
4
+ module Monad
5
+ module ChainableMonad
6
+ def method_missing(name, *args, &fun)
7
+ self.bind { |o| o.send(name, *args, &fun) }
8
+ end
9
+
10
+ def respond_to? name
11
+ self._canBind?(name)
12
+ end
13
+
14
+ def _
15
+ self.unwrap
16
+ end
17
+
18
+ def coerce(other)
19
+ return self, other
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,154 @@
1
+ # Future wraps an asynchronous process and acts as a placeholder.
2
+ #
3
+ # A simple example:
4
+ # <tt>f = Future.eventually { call_remote_web_service }
5
+ # data = f.bind { |response| response.getContent }
6
+ # while data.pending?
7
+ # puts "Still waiting..."
8
+ # sleep 1
9
+ # end
10
+ # puts "Got a response: #{ data.unwrap }" </tt>
11
+ #
12
+ # Rather than explicitly waiting, you can always block on an answer:
13
+ # <tt>data = f.bind { |r| r.getContent }
14
+ # puts "Got a response: #{ data.finalize.unwrap }"</tt>
15
+
16
+ module Mon
17
+
18
+ module Monad
19
+
20
+ require_relative 'chainable_monad'
21
+ require_relative 'monad'
22
+
23
+ class Future < Monad
24
+ include ChainableMonad
25
+
26
+ # Create a Future, executing some function, with optional arguments.
27
+ # Either:
28
+ # <tt>value = Future::eventually { do_something_slow }</tt>
29
+ # Or:
30
+ # <tt>value = Future::eventually(myValue) { |val| do_something_slow(val) }</tt>
31
+ def self::eventually *args, &fun
32
+ FuturePromise::perform(fun, args)
33
+ end
34
+
35
+ # Wrap an object in a Future. It will start off as complete, but functions that you bind
36
+ # to it will be asynchronous.
37
+ # <tt>value = Future["some_username"]
38
+ # futureBirthday = value.bind { |username| db.fetchUserInfo(username).getBirthday } # Returns a FuturePromise, wrapping an inflight thread
39
+ # puts "We have a birthday for some_username: #{ futureBirthday.unwrap }"</tt>
40
+ def self::[](obj)
41
+ FutureComplete[obj]
42
+ end
43
+
44
+ def eql? o
45
+ # Time to collapse
46
+ if o.is_a? Future
47
+ self.unwrap == o.unwrap
48
+ else
49
+ self.unwrap == o
50
+ end
51
+ end
52
+
53
+ def equals? o
54
+ eql? o
55
+ end
56
+
57
+ def == o
58
+ eql? o
59
+ end
60
+
61
+ # For use with contracts, DEPRECATED
62
+ def self::valid?(v)
63
+ v.is_a?(Mon::Future)
64
+ end
65
+
66
+ class << self
67
+ protected :new
68
+ end
69
+ end
70
+
71
+ # FuturePromise represents an asynchronous operation that is still inflight,
72
+ # or complete.
73
+ class FuturePromise < Future
74
+ def initialize(thread)
75
+ @thread = thread
76
+ end
77
+
78
+ # Equivalent to calling Future::eventually { ... }
79
+ def self::perform(fun, args)
80
+ if args.length == 1 and args[0].is_a? FuturePromise
81
+ # We're waiting on something, but we don't want to block
82
+ FuturePromise.new(Thread.new { fun.call(args[0].unwrap) })
83
+ else
84
+ FuturePromise.new(Thread.new(args) { |args| fun.call(*args) })
85
+ end
86
+ end
87
+
88
+ # Take a block, and apply it to the value asynchronously, returing
89
+ # a future to represent the result
90
+ def bind &fun
91
+ FuturePromise::perform(fun, [self])
92
+ end
93
+
94
+ # Block, then return the result (unwrapped)
95
+ def unwrap
96
+ @thread.value
97
+ end
98
+
99
+ # Block until the operation completes, then return the result (wrapped as a FutureComplete)
100
+ def finalize
101
+ value = @thread.value
102
+ FutureComplete[(value.is_a? Future) ? value.unwrap : value]
103
+ end
104
+
105
+ # Are we still inflight?
106
+ def pending?
107
+ @thread.alive?
108
+ end
109
+
110
+ def to_s
111
+ "FuturePromise[#{ @thread }]"
112
+ end
113
+
114
+ class << self
115
+ protected :new
116
+ end
117
+ end
118
+
119
+ # FutureComplete represents a finalized value.
120
+ class FutureComplete < Future
121
+ def initialize(value)
122
+ @value = value
123
+ end
124
+
125
+ # You should probably be using Future[...] instead.
126
+ def self::[](value)
127
+ self::new(value)
128
+ end
129
+
130
+ # Asyrchronously apply fun to the value wrapped by this FutureComplete. Returns a FuturePromise.
131
+ def bind &fun
132
+ FuturePromise::perform(fun, [@value])
133
+ end
134
+
135
+ def pending?
136
+ false
137
+ end
138
+
139
+ def unwrap
140
+ @value
141
+ end
142
+
143
+ def to_s
144
+ "FutureComplete[#{ @value }]"
145
+ end
146
+
147
+ class << self
148
+ protected :new
149
+ end
150
+ end
151
+
152
+ end
153
+
154
+ end
@@ -0,0 +1,200 @@
1
+ # Lazy wraps computations which will only be performed on-demand.
2
+ #
3
+ # A simple example:
4
+ # <tt>l = Lazy.eventually(n) { perform_some_slow_operation(n) }
5
+ # data = f.bind { |result| some_even_more_costly_operation(result) }
6
+ # # At this point, no real computation has taken place
7
+ # puts "Here's the result: #{ data.unwrap } # ouch!</tt>
8
+ #
9
+ # Why would you ever want to do this? Here's an example:
10
+ # <tt>factNums = (0..100000).map { |i| Lazy[i] }.map { |m| m.bind { |i| i.factorial } }
11
+ # # We now have a list of 100000 'potential' factorial numbers, but
12
+ # # we've done this very quickly: we haven't actually done any math yet.
13
+ # fiveThousandFact = factNums[5000].unwrap
14
+ # # We've performed exactly one factorial operation! And yet we can pass around
15
+ # # and use factNums as if it really was a list of factorials. Neat!</tt>
16
+
17
+ module Mon
18
+
19
+ module Monad
20
+
21
+ require_relative 'chainable_monad'
22
+ require_relative 'monad'
23
+
24
+ # Lazy is the parent class of Pending and Final, the two states a Lazy monad can be in.
25
+ # Use with:
26
+ # <tt>lazyValue = Lazy[5] # Seems pointless so far...
27
+ # lazyCalc = lazyValue.bind { |i| (0..i).map { |n| n.factorial } }.bind { |factlist| factlist.map { |i| i * i }.... # Keep right on going!
28
+ # # We still haven't done any work!
29
+ # puts lazyCalc.unwrap # Time to have a nap...</tt>
30
+ # Or:
31
+ # lazyProc = Lazy.eventually(5) { (0..5).map { |i| call_some_remote_service_ondemand(i) } }
32
+ # # Haven't done anything yet...
33
+ # lazyProc.sample.unwrap.map { |v| "A random response: #{ v }" } # Do one of 5 possible service calls</tt>
34
+ class Lazy < Monad
35
+ include ChainableMonad
36
+
37
+ # Wrap a value in Lazy
38
+ def self::[](obj = nil)
39
+ if obj.is_a? Proc
40
+ eventually(obj)
41
+ else
42
+ Final[obj]
43
+ end
44
+ end
45
+
46
+ # Perform an operation, if necessary:
47
+ # <tt>Lazy.eventually { 10 * 10 }</tt>
48
+ # Or:
49
+ # <tt>Lazy.eventually(10) { |n| n * 10 }</tt>
50
+ def self::eventually(*args, &fun)
51
+ Pending::eventually(fun, args)
52
+ end
53
+
54
+ # For contracts. Deprecated!
55
+ def self::valid?(v)
56
+ v.is_a? Mon::Lazy
57
+ end
58
+
59
+ class << self
60
+ protected :new
61
+ end
62
+ end
63
+
64
+ # Final represents a finalized lazy value (i.e. a value with no pending ops).
65
+ class Final < Lazy
66
+ def initialize(obj)
67
+ @obj = obj
68
+ end
69
+
70
+ # You probably want Lazy[...]
71
+ def self::[](obj = nil)
72
+ Final.new(obj)
73
+ end
74
+
75
+ # Add an operation to be performed on the wrapped value, only if necessary
76
+ def bind(&fun)
77
+ Pending::eventually(fun, [@obj])
78
+ end
79
+
80
+ # Perform any pending lazy operations
81
+ def finalize
82
+ self
83
+ end
84
+
85
+ def _canBind?(name)
86
+ @obj.respond_to? name
87
+ end
88
+
89
+ def _finalized?
90
+ true
91
+ end
92
+
93
+ # Perform any pending lazy operations and unwrap the result
94
+ def unwrap
95
+ @obj
96
+ end
97
+
98
+ # Alias for #unwrap
99
+ def _
100
+ unwrap
101
+ end
102
+
103
+ def to_s
104
+ "Final[#{ @obj }]"
105
+ end
106
+
107
+ def eql? o
108
+ # Time to collapse
109
+ if o.is_a? Lazy
110
+ self.unwrap == o.unwrap
111
+ else
112
+ self.unwrap == o
113
+ end
114
+ end
115
+
116
+ def equals? o
117
+ eql? o
118
+ end
119
+
120
+ def == o
121
+ eql? o
122
+ end
123
+
124
+ class << self
125
+ protected :new
126
+ end
127
+ end
128
+
129
+ # Pending represents a Lazy value with pending operations. Unwrapping or finalizing with trigger
130
+ # said pending operations.
131
+ class Pending < Lazy
132
+ def initialize(fun, target = nil)
133
+ case fun.arity
134
+ when 1 then @fun = lambda { fun.call(target.unwrap) }
135
+ when 0 then @fun = fun
136
+ else raise ArgumentError.new("Bad function passed to #{ self }")
137
+ end
138
+ end
139
+
140
+ # Add an operation to be performed on the wrapped value, only if necessary
141
+ def bind(&fun)
142
+ Pending::eventually(fun, [self])
143
+ end
144
+
145
+ def _canBind?(name)
146
+ true
147
+ end
148
+
149
+ # Complete any pending operations and return the result, wrapped in a Final.
150
+ # Eg: <tt>Lazy(10).bind { |i| i * i }.finalize # ==> Final[100]</tt>
151
+ def finalize
152
+ Final[unwrap]
153
+ end
154
+
155
+ # Complete any pending operations and return the result, unwrapped.
156
+ # Eg: <tt>Lazy(10).bind { |i| i * i }.unwrap # ==> 100</tt>
157
+ def unwrap
158
+ @fun.call
159
+ end
160
+
161
+ # Alias for #unwrap
162
+ def _
163
+ unwrap
164
+ end
165
+
166
+ # Perform an operation, if necessary:
167
+ # <tt>Lazy.eventually { 10 * 10 }</tt>
168
+ # Or:
169
+ # <tt>Lazy.eventually(10) { |n| n * 10 }</tt>
170
+ def self::eventually fun, args
171
+ Pending.new(lambda { fun.call(*args.map { |v| (v.is_a? Lazy) ? v.unwrap : v }) })
172
+ end
173
+
174
+ def to_s
175
+ "Pending[#{ @fun }]"
176
+ end
177
+
178
+ def eql? o
179
+ # Time to collapse
180
+ if o.is_a? Lazy
181
+ self.unwrap == o.unwrap
182
+ else
183
+ self.unwrap == o
184
+ end
185
+ end
186
+
187
+ def equals? o
188
+ eql? o
189
+ end
190
+
191
+ def == o
192
+ eql? o
193
+ end
194
+
195
+ class << self
196
+ protected :new
197
+ end
198
+ end
199
+ end
200
+ end