consequence 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3038f8d2f6c5083a3e945054dd080b655507ae47
4
- data.tar.gz: 16ec4fc46d518cadd43564e87acb3bca08e4c8c6
3
+ metadata.gz: 2213b692e96e63757d2f58234f110e448be7a1ef
4
+ data.tar.gz: 38ddbf1267f4e1d68d5d3e87e1967a7aeef4ff9d
5
5
  SHA512:
6
- metadata.gz: fdf17369f753147abb4d225f9690e6206235f506d82a346987aee3c8b89f97084592a524d6a9ef6f2d9506936553793707987104d6d1f6b1df4df543483530d6
7
- data.tar.gz: f79925766a50f40b0d63e62cc5dc5f39e4d53d48ea60a85161dabc292f247562eecb682a483cb0a39e1479f0fcf6aa3ad139d0698ac7f00bef03a188549d64dd
6
+ metadata.gz: da489d84fb8b756fa681a34f9fcade73d7af4b1aace229b95ad627d1682484ebdfd52d95948df7ccec6356273c294c03712718cd019320200f19fa477bb16f63
7
+ data.tar.gz: 3a3d53e75a35fb4baf47acf9723713edb91f9618a4d6664e0408b2e9065ee191e1c76fd5c01f12c13aa4018e09f0b65b9636d35ba64df5f118ef7df607b17867
data/README.md CHANGED
@@ -6,121 +6,213 @@ Simple monad implementation with clear and consistent syntax.
6
6
 
7
7
  ## Usage
8
8
 
9
+ A monad has a value that it is wrapped around. Its value can be anything: String, Module, Proc etc...
10
+ It takes its value as its only argument and can be initialized using the element reference syntax:
11
+
9
12
  ``` ruby
10
13
  require 'consequence'
14
+ include Consequence
11
15
 
12
- def process(i)
13
- Foo[i] >> :next << log >> validate
14
- end
16
+ my_monad = Monad[4]
17
+ ```
15
18
 
16
- def log
17
- ->(v) { puts v }
18
- end
19
+ Its value can be retrieved with the `#value` getter method.
19
20
 
20
- def validate
21
- ->(v, m) { m == Foo[5] ? m : Bar['Fail'] }
22
- end
21
+ ``` ruby
22
+ my_monad.value # 4
23
+ ```
23
24
 
24
- Foo = Class.new(Consequence::Monad)
25
- Bar = Class.new(Consequence::Monad)
25
+ To create new monad types, simply inherit from `Monad`.
26
26
 
27
- p process(0) # Bar['Fail']
28
- p process(4) # Foo[5]
27
+ ``` ruby
28
+ Foo = Class.new(Monad)
29
+ Bar = Class.new(Monad)
29
30
  ```
30
31
 
31
- ## Operations
32
+ A monad is equal to another monad only if both it's type and value are equal.
32
33
 
33
- <dl>
34
- <dt> &gt;&gt; Right Shift </dt>
35
- <dd>Chains a proc with the result being passed along. If the result is not a monad, it is wrapped up in one.</dd>
34
+ ``` ruby
35
+ Foo[0] == Foo[1] # false
36
+ Foo[0] == Bar[0] # false
37
+ Foo[0] == Foo[0] # true
38
+ ```
36
39
 
37
- <dt> &lt;&lt; Left Shift </dt>
38
- <dd>The supplied proc is applied with the result being ignored and the unchanged monad is passed down the chain.</dd>
39
- </dl>
40
+ ### Operations
40
41
 
41
- If the proc accepts one argument, it is passed only the `value` of the monad. If it accepts two arguments, it is passed both the `value` and the monad.
42
+ Monads have two main operations `>>` and `<<`.
42
43
 
43
- Before being called, the proc have their `#to_proc` method called. This allows a `Symbol` to be passed in, whose `#to_proc` method sends the symbol as a message to the `value` of the monad.
44
+ Both take a proc for their only argument. The supplied proc can take 0-2 arguments and the monad will match them, even if it's a lamda or method object with arity checking.
44
45
 
45
- ## Types
46
+ It's first argument will be passed the monads value:
46
47
 
47
- ### Success & Failure
48
+ ``` ruby
49
+ Foo[4] << ->(v) { puts v }
50
+ # $ 4
51
+ ```
48
52
 
49
- A `Success` monad wraps up all exceptions in a `Failed` monad and a `Failed` monad ignores all chained methods. This allows all possible failures in a long process to be dealt with at the end.
53
+ It's second argument will be passed the monad itself. This is useful for making decisions based on the monads type.
50
54
 
51
55
  ``` ruby
52
- require 'consequence'
56
+ Foo[0] << ->(v, m) { puts v if m.is_a?(Foo) }
57
+ # $ 0
58
+ ```
59
+
60
+ ### `>>`
61
+
62
+ The `>>` operation may be variously thought of as the 'map', 'bind' or 'join' operations, but has been left unnamed to avoid any specific connotations.
63
+
64
+ It takes the result of the proc given to it and passes it on:
65
+
66
+ ``` ruby
67
+ Foo[0] >> ->(v) { Bar[v] } # Bar[0]
68
+ ```
69
+
70
+ If it return's a result that is not a monad, it is wrapped up in one so the chain can continue:
71
+
72
+ ``` ruby
73
+ Foo[0] >> ->(v) { v + 1 } # Foo[1]
74
+ ```
75
+
76
+ ### `<<`
77
+
78
+ The `<<` operation may be thought of as the 'pipe' operation.
79
+
80
+ It calls the proc given to it, but ignores it's return value:
81
+
82
+ ``` ruby
83
+ Foo[0] << ->(v) { v + 1 } # Foo[0]
84
+ ```
85
+
86
+ This is useful for creating 'side effects', such as logging or assigning instance variables:
87
+
88
+ ``` ruby
89
+ Foo[0] << ->(v) { @side_effect = v + 1 } # Foo[0]
90
+ puts @side_effect
91
+ # $ 1
92
+ ```
53
93
 
54
- module CreateUser
55
- class << self
56
- include Consequence
57
- alias_method :m, :method
94
+ ### `#to_proc`
58
95
 
59
- def create(attributes)
60
- Success[attributes] >> m(:build) >> m(:validate) >> m(:persist)
61
- end
96
+ Before either operation calls a proc, the `#to_proc` method is called on it. This can be useful if the operation is given an object that is no a proc, but has a `#to_proc` method that returns one.
62
97
 
63
- private
98
+ A good example of this is the Symbol object, whose `#to_proc` method supplies a proc that sends the symbol as a message to its first argument. In this case this means calling that method on the value:
64
99
 
65
- def build(attributes)
66
- User.new(attributes)
67
- end
100
+ ``` ruby
101
+ Foo[[1, 4, 7]] >> :pop # Foo[7]
102
+ ```
68
103
 
69
- def validate(user)
70
- validator = UserValidator.new(user)
71
- validator.valid? ? Success[user] : Failure[validator.errors]
72
- end
104
+ This also can be used to make a composition syntax, by writing a `#to_proc` method for a monad:
73
105
 
74
- def persist(user)
75
- user.save
76
- end
106
+ ``` ruby
107
+ class Add < Monad
108
+ def to_proc
109
+ ->(v) { v + value }
77
110
  end
78
111
  end
112
+
113
+ Add[1] >> Add[4] >> Add[6] # Add[11]
79
114
  ```
80
115
 
81
- If the `User#new` raises an exception, the `validate` and `persist` methods won't be called, and a `Failure` monad will be returned with the exception as it's `value`.
116
+ ## Built-In Types
82
117
 
83
- If the `validator` finds the new user to be invalid, the `persist` method will not be called and a `Failure` monad will be returned with the errors as it's `value`.
118
+ ### NullMonad
84
119
 
85
- ### Something & Nothing
120
+ The `NullMonad` is a monad whose `>>` and `<<` operations have been overriden to ignore all input:
121
+
122
+ ``` ruby
123
+ NullMonad[0] >> ->(v) { v + 1 } # Consequence::NullMonad[0]
124
+ ```
86
125
 
87
- A `Something` monad wraps up a nil result in a `Nothing` monad and a `Nothing` monad ignores all chained methods. This prevents `MissingMethod` errors from trying to be call a method on `nil`.
126
+ This is different from the `<<` operation, because the proc isn't even run, so can cause no side effects:
88
127
 
89
- ## Utils
128
+ ``` ruby
129
+ NullMonad[0] << ->(v) { @side_effect = v + 1 }
130
+ puts @side_effect.inspect
131
+ # $ nil
132
+ ```
133
+
134
+ For both operations, the `NullMonad` returns itself, so a chain of operations can be called on it without causing an error. This can be useful for stoping a chain midway through safely:
135
+
136
+ ``` ruby
137
+ Continue = Class.new(Monad)
138
+ Stop = Class.new(NullMonad)
139
+
140
+ drop_first = ->(v) { v[1..-1] }
141
+ check_empty = ->(v, m) { v.empty? ? Stop[v] : m }
142
+
143
+ Continue[[1, 3, 4]] >> drop_first >> check_empty >> # Continue[[3, 4]]
144
+ drop_first >> check_empty >> # Continue[[4]]
145
+ drop_first >> check_empty >> # Stop[[]]
146
+ drop_first >> check_empty # Stop[[]]
147
+ ```
90
148
 
91
- ### `send_to_value` and `send_to_monad`
149
+ ### Success & Failure
92
150
 
93
- `send_to_value` and `send_to_monad` are utility methods that can be included from `Consequence::Utils`. They create procs that pass their arguments to the send method on the value and monad respectively.
151
+ A `Success` monad wraps up all exceptions in a `Failure` monad:
94
152
 
95
153
  ``` ruby
96
- include Consequence::Utils
154
+ Success[0] >> ->(v) { 5 / v }
155
+ # Consequence::Failure[#<ZeroDivisionError: divided by 0>]
156
+ ```
157
+
158
+ A `Failure` monad is a subclass of the `NullMonad` so all successive chained procs are ignored.
97
159
 
98
- Monad[2] << send_to_value(:**, 3) # Monad[8]
160
+ Both `Success` and `Failure` respond to the `#succeeded?` and `#failed?` query methods in the way you'd expect:
161
+
162
+ ``` ruby
163
+ Success[0].succeeded? # true
164
+ Success[0].failed? # false
165
+ Failure[0].succeeded? # false
166
+ Failure[0].failed? # true
99
167
  ```
100
168
 
101
- To include into Object to be available within all objects:
169
+ For an example, check out the [Success & Failure example](https://github.com/mushishi78/consequence/wiki/Success-&-Failure-Example) on the wiki.
170
+
171
+ ### Something & Nothing
172
+
173
+ A `Something` monad wraps up a nil result in a `Nothing` monad:
102
174
 
103
175
  ``` ruby
104
- require 'consequence/core_ext/utils'
176
+ Something[[1, 3, 5]] >> ->(v) { v[4] } # Consequence::Nothing[nil]
105
177
  ```
106
178
 
107
- ### `Object#m`
179
+ A `Nothing` monad is also a subclass of the `NullMonad` so all successive chained procs are ignored. This prevents `MissingMethod` errors from trying to call a method on a `nil`.
108
180
 
109
- As a shorthand for the `Object#method` method, it can be useful to alias this to m, such as in the Success/Failure example. To do this for all objects:
181
+ A `Nothing` responds positively to the `#nil?` method:
110
182
 
111
183
  ``` ruby
112
- require 'consequence/core_ext/m_alias'
184
+ Nothing[nil].nil? # true
113
185
  ```
114
186
 
115
- ### `Symbol#to_proc`
187
+ ## DelegatesToValue
116
188
 
117
- `Symbol#to_proc` can't be used to call private methods. This is inconvenient if you want an object to wrap itself up and call it's oen private methods with symbols. To use that style of notation, you can add this:
189
+ `DelegatesToValue` is a module that can be included into Monad that adds a `#method_missing` method to delegate calls to its value:
118
190
 
119
191
  ``` ruby
120
- require 'consequence/core_ext/private_symbol_proc'
192
+ Foo.include DelegatesToValue
193
+ Foo[[1, 4, 6]].map {|n| n + 1} # Foo[[2, 5, 7]]
121
194
  ```
122
195
 
123
- Alternatively, you can use `object#method`.
196
+ It delegates via the `>>` operation, so subclasses of the NullMonad will respond to delegated method calls, but still take no action:
197
+
198
+ ``` ruby
199
+ Something.include DelegatesToValue
200
+ Nothing.include DelegatesToValue
201
+
202
+ dangrous_hash = {user: {orders: {1 => {price: 3.99} } } }
203
+
204
+ Something[dangrous_hash][:user][:orders][1][:price] # Consequence::Something[3.99]
205
+ Something[dangrous_hash][:user][:orders][2][:price] # Consequence::Nothing[nil]
206
+ ```
207
+
208
+ ## Wiki
209
+
210
+ To find some examples and information about utils, [check out the wiki](https://github.com/mushishi78/consequence/wiki/Consequence) and feel free to contribute to it.
211
+
212
+ ## Inspirations
213
+
214
+ * [Deterministic](https://github.com/pzol/deterministic)
215
+ * [Kleisli](https://github.com/txus/kleisli)
124
216
 
125
217
  ## Installation
126
218
 
data/lib/consequence.rb CHANGED
@@ -2,6 +2,7 @@ module Consequence
2
2
  end
3
3
 
4
4
  require 'consequence/monad'
5
- require 'consequence/utils'
6
5
  require 'consequence/success'
7
6
  require 'consequence/something'
7
+ require 'consequence/delegates_to_value'
8
+ require 'consequence/utils'
@@ -0,0 +1,16 @@
1
+ require 'consequence/monad'
2
+
3
+ module Consequence
4
+ module DelegatesToValue
5
+ def method_missing(method_name, *args, &b)
6
+ return super unless value.respond_to?(method_name)
7
+ self >> -> { value.send(method_name, *args, &b) }
8
+ end
9
+
10
+ protected
11
+
12
+ def respond_to_missing?(method_name, include_private = false)
13
+ value.respond_to?(method_name, include_private) || super
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,7 @@
1
1
  module Consequence
2
2
  module Utils
3
+ alias_method :m, :method
4
+
3
5
  def send_to_value(*args)
4
6
  ->(v) { v.send(*args) }
5
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: consequence
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Max White
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-15 00:00:00.000000000 Z
11
+ date: 2015-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -39,9 +39,8 @@ files:
39
39
  - LICENSE.txt
40
40
  - README.md
41
41
  - lib/consequence.rb
42
- - lib/consequence/core_ext/m_alias.rb
43
- - lib/consequence/core_ext/private_symbol_proc.rb
44
42
  - lib/consequence/core_ext/utils.rb
43
+ - lib/consequence/delegates_to_value.rb
45
44
  - lib/consequence/monad.rb
46
45
  - lib/consequence/something.rb
47
46
  - lib/consequence/success.rb
@@ -1,3 +0,0 @@
1
- class Object
2
- alias_method :m, :method
3
- end
@@ -1,9 +0,0 @@
1
- # This allows symbol#to_proc method to call private functions.
2
- # Which can be useful if you want to have a class wrap itself in
3
- # a Monad and call it private methods.
4
-
5
- class Symbol
6
- def to_proc
7
- ->(v) { v.send(self) }
8
- end
9
- end