Functional Programming in Ruby
Libraries: dry-rb (dry-monads, dry-types, dry-struct, dry-validation), Hamster/immutable-ruby (persistent collections).
# Immutability
POINT = { x: 1, y: 2 }.freeze
Money = Data.define(:amount, :currency) # immutable values
list = [1, 2, 3].freeze
new_list = list + [4] # non-destructive operations
# Pure functions and composition
normalize = ->(s) { s.strip.downcase }
slugify = ->(s) { s.gsub(/\s+/, "-") }
to_slug = normalize >> slugify
to_slug.(" Hello World ") # "hello-world"
# Higher-order functions
def apply_n(f, n, x) = n.times.reduce(x) { |acc, _| f.(acc) }
apply_n(->(v) { v * 2 }, 3, 1) # 8
# Currying and partial application
log = ->(level, msg) { "[#{level}] #{msg}" }.curry
warn_log = log["WARN"]
warn_log["disk almost full"]
# Method references
%w[1 2 3].map(&method(:Integer))
# Memoization
@cache = {}
def slow_square(n) = @cache[n] ||= (sleep 0.1; n * n)
# Lazy, infinite sequences
primes = (2..).lazy.select { |n| (2..Math.sqrt(n)).none? { n % it == 0 } }
primes.first(10)
# then / yield_self pipelines
"42".then { Integer(it) }.then { it * 2 } # 84
user.tap { |u| log(u) }.save # tap: side effect, returns the receiver