funcify 0.4.25

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,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