mon 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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