Module: Dorm::FunctionalHelpers

Defined in:
lib/dorm/functional_helpers.rb

Overview

Functional composition helpers - inspired by Clojure

Constant Summary collapse

Some =
Data.define(:value) do
  def bind(&block)
    result = block.call(value)
    result.is_a?(Some) || result.is_a?(None) ? result : Some.new(result)
  end

  def map(&block)
    Some.new(block.call(value))
  end

  def value_or(_default)
    value
  end

  def some? = true
  def none? = false
end
None =
Data.define do
  def bind(&block)
    self
  end

  def map(&block)
    self
  end

  def value_or(default)
    default
  end

  def some? = false
  def none? = true
end

Class Method Summary collapse

Class Method Details

.apply_if(value, condition, func) ⇒ Object

Apply function if condition is true, otherwise return original value



72
73
74
# File 'lib/dorm/functional_helpers.rb', line 72

def apply_if(value, condition, func)
  condition ? func.call(value) : value
end

.comp(*functions) ⇒ Object

Compose functions into a single function comp(f1, f2, f3) returns ->(x) { f1(f2(f3(x))) }



16
17
18
# File 'lib/dorm/functional_helpers.rb', line 16

def comp(*functions)
  ->(x) { functions.reverse.reduce(x) { |v, f| f.call(v) } }
end

.drop(collection, n) ⇒ Object

Drop first n elements



57
58
59
# File 'lib/dorm/functional_helpers.rb', line 57

def drop(collection, n)
  collection.drop(n)
end

.filter(collection, predicate) ⇒ Object

Filter collection with predicate



32
33
34
# File 'lib/dorm/functional_helpers.rb', line 32

def filter(collection, predicate)
  collection.select(&predicate)
end

.find_first(collection, predicate) ⇒ Object

Find first element matching predicate



47
48
49
# File 'lib/dorm/functional_helpers.rb', line 47

def find_first(collection, predicate)
  collection.find(&predicate)
end

.group_by_fn(collection, grouper) ⇒ Object

Group by a function result



62
63
64
# File 'lib/dorm/functional_helpers.rb', line 62

def group_by_fn(collection, grouper)
  collection.group_by(&grouper)
end

.juxt(*functions) ⇒ Object

Juxt - apply multiple functions to same value, return array of results juxt(f1, f2, f3) returns ->(x) { [f1(x), f2(x), f3(x)] }



97
98
99
# File 'lib/dorm/functional_helpers.rb', line 97

def juxt(*functions)
  ->(x) { functions.map { |f| f.call(x) } }
end

.map_over(collection, transform) ⇒ Object

Map over collection



37
38
39
# File 'lib/dorm/functional_helpers.rb', line 37

def map_over(collection, transform)
  collection.map(&transform)
end

.maybe(value) ⇒ Object

Maybe monad helpers for dealing with nils



102
103
104
# File 'lib/dorm/functional_helpers.rb', line 102

def maybe(value)
  value.nil? ? None.new : Some.new(value)
end

.partial(func, *args) ⇒ Object

Partial application - fix some arguments partial(method(:add), 5) returns ->(x) { add(5, x) }



22
23
24
25
26
27
28
29
# File 'lib/dorm/functional_helpers.rb', line 22

def partial(func, *args)
  # ->(x) { func.call(*args, x) }
  if [method(:filter), method(:map_over), method(:take)].include?(func)
    ->(x) { func.call(x, *args) }
  else
    ->(x) { func.call(*args, x) }
  end
end

.pipe(value, *functions) ⇒ Object

Pipe value through a series of functions pipe(value, f1, f2, f3) equivalent to f3(f2(f1(value)))



10
11
12
# File 'lib/dorm/functional_helpers.rb', line 10

def pipe(value, *functions)
  functions.reduce(value) { |v, f| f.call(v) }
end

.reduce_with(collection, initial, reducer) ⇒ Object

Reduce collection



42
43
44
# File 'lib/dorm/functional_helpers.rb', line 42

def reduce_with(collection, initial, reducer)
  collection.reduce(initial, &reducer)
end

.sort_by_fn(collection, sorter) ⇒ Object

Sort by a function result



67
68
69
# File 'lib/dorm/functional_helpers.rb', line 67

def sort_by_fn(collection, sorter)
  collection.sort_by(&sorter)
end

.take(collection, n) ⇒ Object

Take first n elements



52
53
54
# File 'lib/dorm/functional_helpers.rb', line 52

def take(collection, n)
  collection.take(n)
end

.thread_first(value, *functions) ⇒ Object

Thread-first macro simulation (Clojure’s ->) thread_first(x, f1, f2, f3) equivalent to f3(f2(f1(x)))



78
79
80
# File 'lib/dorm/functional_helpers.rb', line 78

def thread_first(value, *functions)
  pipe(value, *functions)
end

.thread_last(value, *functions) ⇒ Object

Thread-last macro simulation (Clojure’s ->>) thread_last(x, f1, f2, f3) equivalent to f3(f2(f1(x))) Useful when you want the value to be the last argument



85
86
87
88
89
90
91
92
93
# File 'lib/dorm/functional_helpers.rb', line 85

def thread_last(value, *functions)
  functions.reduce(value) do |v, f|
    if f.respond_to?(:curry)
      f.curry.call(v)
    else
      f.call(v)
    end
  end
end