funcify 0.4.25

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ module Funcify
2
+
3
+ class Cond
4
+
5
+ class << self
6
+
7
+ # The little Either Cond
8
+ # returns either the result of fn_ok || fn_fail by applying the value to test <t>.
9
+ # > either.(Monad.maybe_value_ok, identity, Monad.maybe_value).(Success(1)) => 1
10
+ def either
11
+ -> test, fn_ok, fn_fail, value { test.(value) ? fn_ok.(value) : fn_fail.(value) }.curry
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,370 @@
1
+ module Funcify
2
+
3
+ class Fn
4
+
5
+ extend Dry::Monads::Try::Mixin
6
+ extend Dry::Monads::Result::Mixin
7
+
8
+ class << self
9
+
10
+ # Common curried map higher order fn
11
+ # > map.(-> i { i.to_s } ).([1,2,3])
12
+ def map
13
+ ->(f, enum) { enum.map {|e| f.(e) } }.curry
14
+ end
15
+
16
+ def fmap
17
+ ->(f, enum) { enum.flat_map {|e| f.(e) } }.curry
18
+ end
19
+
20
+ def sequence
21
+ ->(fs, i) { fs.inject([]) { |r, f| r << f.(i) } }.curry
22
+ end
23
+
24
+ def inject
25
+ -> acc, f, xs { xs.inject(acc) {|acc, x| f.(acc).(x) } }.curry
26
+ end
27
+
28
+ def group_by
29
+ -> f, xs { xs.group_by { |x| f.(x) } }.curry
30
+ end
31
+
32
+ def merge
33
+ -> to, with { to.merge(with) }.curry
34
+ end
35
+
36
+ # Curryed fn that removes elements from a collection where f.(e) is true
37
+ def remove
38
+ ->(f, enum) { enum.delete_if {|e| f.(e) } }.curry
39
+ end
40
+
41
+ # add an element to an array
42
+ def add
43
+ -> x, xs { xs << x }.curry
44
+ end
45
+
46
+ # finds the first element in a collecton where f.(e) is true
47
+ def find
48
+ ->(f, enum) { enum.find { |e| f.(e) } }.curry
49
+ end
50
+
51
+ def select
52
+ ->(f, enum) { enum.select { |e| f.(e) } }.curry
53
+ end
54
+
55
+ def replace
56
+ ->(r, with, s) { s.gsub(r,with) }.curry
57
+ end
58
+
59
+ def join
60
+ -> sep, i { i.join(sep) }.curry
61
+ end
62
+
63
+ def split
64
+ -> sep, i { i.split(sep) }.curry
65
+ end
66
+
67
+ def max
68
+ ->(f, enum) { f.(enum).max }.curry
69
+ end
70
+
71
+ def all?
72
+ ->(f, enum) { enum.all? { |e| f.(e) } }.curry
73
+ end
74
+
75
+ def any?
76
+ ->(f, enum) { enum.any? { |e| f.(e) } }.curry
77
+ end
78
+
79
+ def none?
80
+ ->(f, enum) { enum.none? { |e| f.(e) } }.curry
81
+ end
82
+
83
+ # > uniq.(identity).([1,1,2])
84
+ def uniq
85
+ -> f, enum { enum.uniq { |e| f.(e) } }.curry
86
+ end
87
+
88
+ # > partition.(-> x { x == 1 }).([1,1,2,3,4])
89
+ def partition
90
+ -> f, enum { enum.partition { |e| f.(e) } }.curry
91
+ end
92
+
93
+ def last
94
+ -> xs { xs.last }
95
+ end
96
+
97
+ def first
98
+ -> xs { xs.first }
99
+ end
100
+
101
+ def rest
102
+ -> xs {
103
+ _a, *b = xs
104
+ b
105
+ }
106
+ end
107
+
108
+ def when_nil?
109
+ ->(i) { i.nil? }
110
+ end
111
+
112
+ def flatten
113
+ -> xs { xs.flatten }
114
+ end
115
+
116
+ # lifts the value, otherwise returns nil
117
+ def lift
118
+ ->(f, with, i) { f.(i) ? with.(i) : nil }.curry
119
+ end
120
+
121
+ # Takes a structure (like a Monad), an OK test fn, and a fn to extract when OK
122
+ # Returns the result of f, otherwise nil
123
+ # > lift_value.(maybe_value_ok, maybe_value),
124
+ def lift_value
125
+ ->(value_type, f) { Fn.lift.(value_type, f) }.curry
126
+ end
127
+
128
+ def lift_monad
129
+ -> value { maybe_value_ok?.(value) ? maybe_value.(value) : maybe_failure.(value) }
130
+ end
131
+
132
+ def identity
133
+ ->(i) { i }
134
+ end
135
+
136
+ def method
137
+ -> m, obj { obj.send(m) }.curry
138
+ end
139
+
140
+ # The little Either Cond
141
+ # returns either the result of f_ok || f_fail by applying the value to test t.
142
+ # > either.(maybe_value_ok, identity, maybe_failure).(Success(1)) => Success(1)
143
+ def either
144
+ ->(test, f_ok, f_fail, value) { test.(value) ? f_ok.(value) : f_fail.(value) }.curry
145
+ end
146
+
147
+ # success_fn: a test fn to apply to the enum resulting from applying the tests; e.g. Fn.all? (and) or Fn.any? (or)
148
+ # test_fns : [test_fn]; each test is called with (value)
149
+ # value : the test context (can be anything understood by the tests)
150
+ # > tests.(all?, [-> x { x == 1}]).(1) => true
151
+ def tests
152
+ -> success_fn, test_fns, value {
153
+ Fn.compose.(
154
+ success_fn.(Fn.identity), # provide a results extractor fn to the success_fn
155
+ Fn.map.(-> test_fn { test_fn.(value) } ), # call each test fn with the context
156
+ ).(test_fns)
157
+ }.curry
158
+ end
159
+
160
+ # the famous compose
161
+ # Applies from right to left, taking the result of 1 fn and injecting into the next
162
+ # No Monads tho!
163
+ # compose.(-> n { n + 1}, -> n { n * 2 }).(10)
164
+ def compose
165
+ ->(*fns) { fns.reduce { |f, g| lambda { |x| f.(g.(x)) } } }
166
+ end
167
+
168
+ # Monadic Compose, using flat_map
169
+ # The result of a fn must return an Either.
170
+ # fmap_compose.([->(v) { M.Success(v + 1) }, ->(v) { M.Success(v + 10) }]).(M.Success(0))
171
+ def fmap_compose
172
+ ->(fns, value) {
173
+ fns.inject(value) {|result, fn| result.success? ? result.fmap(fn).value_or : result}
174
+ }.curry
175
+ end
176
+
177
+ # reverse version of fmap_compose
178
+ def fmapr_compose
179
+ ->(*fns) {
180
+ -> x { fns.reverse.inject(x) {|x, fn| x.success? ? x.fmap(fn).value_or : x} }
181
+ }
182
+ end
183
+
184
+ # Apply that takes a function and an enum and applies the fn to the entire enum
185
+ # Works with methods like #join, #split
186
+ def apply
187
+ ->(f, enum) { f.(enum) }.curry
188
+ end
189
+
190
+ # + field; the property to extract from the record. Either a String/Symb or a Proc which takes the record
191
+ # + test_value; the value which has == applied to determine equality
192
+ # + i; the record under test
193
+ # e.g. equality.(:a).("equal").({a: "equal"})
194
+ # e.g. equality.(test_fn).("equal").({a: "equal"})) ; where test_fn is -> x { x[:a] }
195
+ def equality
196
+ ->( field, test_value, i ) {
197
+ if field.kind_of?(Proc)
198
+ field.(i) == test_value
199
+ else
200
+ i[field] == test_value
201
+ end
202
+ }.curry
203
+ end
204
+
205
+ # x can either be an array or a string
206
+ def include
207
+ -> x, v { x.include?(v) }.curry
208
+ end
209
+
210
+ # Right Include, where the value is applied partially waiting for the test prop
211
+ def rinclude
212
+ -> v, x { x.include?(v) }.curry
213
+ end
214
+
215
+
216
+ def linclusion
217
+ ->( field, value, i ) { i[field].include?(value) }.curry
218
+ end
219
+
220
+ # takes a regex and applies it to a value
221
+ def match
222
+ ->(r, i) { i.match(r) }.curry
223
+ end
224
+
225
+ def take
226
+ ->(f, i) { f.(i) unless i.nil? }.curry
227
+ end
228
+
229
+ # right at; takes the key/index and applies the enum
230
+ def at
231
+ ->(x, i) { i[x] unless i.nil? }.curry
232
+ end
233
+
234
+ # left at; takes the enum and applies the key/index to it.
235
+ def lat
236
+ ->(i, x) { i[x] }.curry
237
+ end
238
+
239
+ def all_keys
240
+ -> h { h.flat_map { |k, v| [k] + (v.is_a?(Hash) ? all_keys.(v) : [v]) } }
241
+ end
242
+
243
+ def coherse
244
+ -> f, xs { map.(-> x { x.send(f) } ).(xs) }.curry
245
+ end
246
+
247
+ def max_int
248
+ -> limit, i { i > limit ? limit : i }.curry
249
+ end
250
+
251
+ def failure
252
+ -> v { Failure(v) }
253
+ end
254
+
255
+ def success
256
+ -> v { Success(v) }
257
+ end
258
+
259
+ def maybe_value_ok?
260
+ ->(v) { v.respond_to?(:success?) && v.success? }
261
+ end
262
+
263
+ def maybe_value_fail?
264
+ -> v { v.respond_to?(:failure?) && v.failure? }
265
+ end
266
+
267
+ def maybe_value
268
+ ->(v) { v.value_or }
269
+ end
270
+
271
+ def maybe_failure
272
+ ->(v) { v.failure }
273
+ end
274
+
275
+ def status_value_ok?
276
+ ->(v) { v.status == :ok }
277
+ end
278
+
279
+ def ctx_value
280
+ ->(v) { v.context }
281
+ end
282
+
283
+ def method_caller
284
+ -> obj, method, v { obj.send(method, v) }.curry
285
+ end
286
+
287
+ def break_point
288
+ -> args { binding.pry }
289
+ end
290
+
291
+ def empty?
292
+ -> xs { xs.empty? }
293
+ end
294
+
295
+ def present?
296
+ -> x { x.present? }
297
+ end
298
+
299
+ def nothing
300
+ -> x { nil }
301
+ end
302
+
303
+ def remove_nil
304
+ Fn.remove.(->(i) { i.nil? } )
305
+ end
306
+
307
+ # f: add fn
308
+ # g: remove fn
309
+ # prev state
310
+ # this state
311
+ def change_set_fn
312
+ -> f, g, prev, this {
313
+ f.(Set.new(this) - Set.new(prev))
314
+ g.(Set.new(prev) - Set.new(this))
315
+ }.curry
316
+ end
317
+
318
+ # Takes a collection and generates a string delimited by the delimiter (such as "|")
319
+ # Returns a curryed fn (ready for the collection) that takes 2 params:
320
+ # @param f, a function that extracts the properties from a map; e.g. F.map.(F.identity)
321
+ # @param enum, the map
322
+ def delimiter_tokeniser
323
+ -> delimiter, f, enum { f.(enum).join(delimiter) }.curry
324
+ end
325
+
326
+ def delimiter_detokeniser
327
+ -> delimiter, f, enum { map.(f, enum).join(delimiter) }.curry
328
+ end
329
+
330
+ def detokeniser(delimiter)
331
+ ->(str) { str.split(delimiter) }.curry
332
+ end
333
+
334
+
335
+ # Provides a Maybe pipeline wrapped in a Lambda. This allows the pipeline functions to be
336
+ # applied first, and returns a function which allows the injection of the params to be applied into the
337
+ # beginning of the pipeline.
338
+ # e.g.
339
+ # pipeline = maybe_pipeline.([-> x { Success(x + 1) } ] )
340
+ # pipeline.value_or.(Success(1)) => Success(2)
341
+ def maybe_pipeline
342
+ ->(pipeline) {
343
+ Success(lambda do |value|
344
+ pipeline.inject(value) do |result, fn|
345
+ result.success? ? result.fmap(fn).value_or : result
346
+ end
347
+ end)
348
+ }
349
+ end
350
+
351
+ # takes a function which is ready to be executed and wraps it in a function which will finally invoke it,
352
+ # by calling with empty arguments
353
+ def wrapper
354
+ -> fn { -> { fn } }
355
+ end
356
+
357
+ def all_success?
358
+ Fn.all?.(Monad.maybe_value_ok?)
359
+ end
360
+
361
+ def hash_to_tokens
362
+ compose.(join.(","), Map.map.(-> k, v { "#{k}:#{v}"}))
363
+ end
364
+
365
+
366
+ end # class Self
367
+
368
+ end # class
369
+
370
+ end # module
@@ -0,0 +1,27 @@
1
+ module Funcify
2
+
3
+ class FSet
4
+
5
+ class << self
6
+
7
+ def subset?
8
+ -> subset, superset { to_set(subset).subset?(to_set(superset)) }.curry
9
+ end
10
+
11
+ def superset?
12
+ -> superset, subset { to_set(superset).superset?(to_set(subset)) }.curry
13
+ end
14
+
15
+ def eq?
16
+ -> seta, setb { to_set(seta) == (to_set(setb)) }.curry
17
+ end
18
+
19
+ def to_set(array_or_set)
20
+ array_or_set.instance_of?(Array) ? Set.new(array_or_set) : array_or_set
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,45 @@
1
+ module Funcify
2
+
3
+ class Map
4
+
5
+ extend Dry::Monads::Try::Mixin
6
+ extend Dry::Monads::Result::Mixin
7
+
8
+ class << self
9
+
10
+ def map
11
+ ->(f, ms) { ms.map {|k,v| f.(k,v) } }.curry
12
+ end
13
+
14
+ def any?
15
+ ->(f, ms) { ms.any? {|k,v| f.(k,v) } }.curry
16
+ end
17
+
18
+ def fmap
19
+ ->(f, ms) { ms.flat_map {|k,v| f.(k,v) } }.curry
20
+ end
21
+
22
+ def inject
23
+ -> j, f, ms { ms.inject(j) {|acc, (k,v)| f.(acc).(k,v) } }.curry
24
+ end
25
+
26
+ def select
27
+ -> f, ms { ms.select {|k,v| f.(k,v) } }.curry
28
+ end
29
+
30
+ def equality
31
+ -> field, test_value, i {
32
+ if field.kind_of?(Proc)
33
+ field.(i) == test_value
34
+ else
35
+ i[field] == test_value
36
+ end
37
+ }.curry
38
+
39
+ end
40
+
41
+ end # class Self
42
+
43
+ end # class
44
+
45
+ end # module