Class: Object

Inherits:
BasicObject
Defined in:
lib/funtools/cons.rb,
lib/funtools/types.rb,
lib/funtools/recursion.rb,
lib/funtools/composition.rb,
lib/funtools/pattern-matching.rb

Instance Method Summary collapse

Instance Method Details

#compose(*f) ⇒ Object

Public: Compose a series of functions.

f - Any number of Procs, Methods, or Symbols which should be composed. If

functions f, g, and h are composed in that order, the result will be
f . g . h -> f(g(h(...)))

Returns a composed Proc.



33
34
35
36
37
# File 'lib/funtools/composition.rb', line 33

def compose(*f)
  f.map do |g|
    g.is_a?(Array) ? ->(n) { g.first.to_proc[n,*g.drop(1)] } : g.to_proc
  end.reduce(&:*)
end

#cons(left, right) ⇒ Object

Public: Wrap Cons.new to construct a new Cons cell.

left - Any Object to be the left element of the cell. right - Any Object to be the right element of the cell.

Returns a Cons cell.



8
9
10
# File 'lib/funtools/cons.rb', line 8

def cons(left, right)
  Cons.new(left, right)
end

#defix(sym, &block) ⇒ Object

Public: Define a method in the current scope which will execute a given block recursively until a fixpoint is reached.

sym - Symbol defining the name of the method to be created. block - Block containing the logic for the function to be created.

Returns nothing.



13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/funtools/recursion.rb', line 13

def defix(sym, &block)
  message   = :define_method if respond_to?(:define_method, true)
  message ||= :define_singleton_method
  self.send(message, sym) do |*n|
    last = nil
    [1].cycle.reduce(n) do |c, e|
      break c if last == c
      last = c
      instance_exec(c, &block)
    end
  end
end

#defpattern(sym) ⇒ Object

Public: Define a method in the current scope which allow pattern matching function declaration to be used.

sym - Symbol defining the name of the method to be created. block - Block containing the logic for the function to be created.

Returns nothing.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/funtools/pattern-matching.rb', line 9

def defpattern(sym)
  match = ->(a, b) do
    if([a,b].map { |o| o.is_a?(Enumerable) && a.class == b.class }.all?)
      raise ArgumentError unless a.length == b.length

      zipped = a.is_a?(Hash) ? a.sort.zip(b.sort) : a.zip(b)

      zipped.reduce(true) do |c, e|
        c && match.(*e)
      end
    else
      a.nil? || a == b
    end
  end

  old_method = self.class.method(sym) if self.class.method_defined?(:sym)
  patterns = []
  self.class.send(:define_method, sym) do |*l, &b|
    patterns << ->(*n) do
      ->(*m) do
        if m.length == n.length
          e = m.zip(n)
          raise NoMatch if e.reject { |e| match.(*e) }.any?
          instance_exec(*n, &b)
        end
      end.(*l)
    end
  end

  yield

  if old_method
    self.class.send(:define_method, sym, &old_method)
  else
    self.class.send(:remove_method, sym)
  end

  message   = :define_method if respond_to?(:define_method, true)
  message ||= :define_singleton_method
  self.send(message, sym) do |*args|
    patterns.each do |pattern|
      begin
        return instance_exec(*args, &pattern)
      rescue NoMatch
      end
    end
    instance_exec { raise NoMatch }
  end
end

#deftail(sym, &block) ⇒ Object

Public: Define a method in the current scope which will execute a given block in such a manner that recursive calls are handled properly so long as the call constitutes tail recursion.

sym - Symbol defining the name of the method to be created. block - Block containing the logic for the function to be created.

Returns nothing.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/funtools/recursion.rb', line 34

def deftail(sym, &block)
  message   = :define_method if respond_to?(:define_method, true)
  message ||= :define_singleton_method

  self.send(message, sym) do |*n|
    instance_exec { self.dup }.instance_exec do
      class << self
        self
      end.class_eval do
        define_method(sym) do |*a|
          a.reduce(Funtools::RecurseArgs.new) { |c,e| c << e }
        end
      end

      loop do
        n = instance_exec(*n, &block)
        return n unless n.is_a?(Funtools::RecurseArgs)
      end
    end
  end
end

#list(first, second = nil, *rest) ⇒ Object

Public: Construct a list of nested Cons cells.

first - Any Object to be the leftmost element of the list. second - Any Object to be the second element of the list (default: nil). rest - Any number of Objects to serve as elements in the list.

Returns a list (nested Cons cells).



19
20
21
22
# File 'lib/funtools/cons.rb', line 19

def list(first, second = nil, *rest)
  set = (rest.empty? && second.nil?) ? [] : rest + [nil]
  ([first, second] + set).reverse.reduce { |c, e| Cons.new(e, c) }
end

#pl(data, *f) ⇒ Object

Public: Mimic the -> macro in Clojure.

data - Any data to be passed to the composed function. f - Any number of Procs, Methods, or Symbols which should be composed.

If functions f, g, and h are composed in that order, the result will
be f . g . h -> f(g(h(...)))

Returns the result of the composed functions, called with data as the argument.



48
49
50
# File 'lib/funtools/composition.rb', line 48

def pl(data, *f)
  compose(*f.reverse)[*[data].flatten]
end