Class: Mercury::Cps

Inherits:
Object
  • Object
show all
Defined in:
lib/mercury/cps.rb,
lib/mercury/cps/seq.rb,
lib/mercury/cps/methods.rb,
lib/mercury/cps/seq_with_let.rb

Defined Under Namespace

Modules: Methods Classes: Seq, SeqWithLet

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(&cps) ⇒ Cps

Returns a new instance of Cps.

Parameters:

  • cps (Proc)

    a CPS proc (signature: *args, &k)



21
22
23
# File 'lib/mercury/cps.rb', line 21

def initialize(&cps)
  @cps = cps
end

Instance Attribute Details

#cpsObject (readonly)

Returns the value of attribute cps.



18
19
20
# File 'lib/mercury/cps.rb', line 18

def cps
  @cps
end

Class Method Details

.concurrently(*cpss) ⇒ Object

Returns a Cps that executes the provided Cpses concurrently. Once all complete, their return values are passed to the continuation in an array with positions corresponding to the provided Cpses.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/mercury/cps.rb', line 73

def self.concurrently(*cpss)
  cpss = Utils.unsplat(cpss)

  Cps.new do |*in_args, &k|
    pending_completions = cpss
    returned_args = []
    cpss.each_with_index do |cps, i|
      cps.run(*in_args) do |*out_args|
        returned_args[i] = out_args
        pending_completions.delete(cps)
        if pending_completions.none?
          k.call(returned_args)
        end
      end
    end
  end
end

.identityObject

The identity function as a Cps.



66
67
68
# File 'lib/mercury/cps.rb', line 66

def self.identity
  new { |*args, &k| k.call(*args) }
end

.inject(xs, &block) ⇒ Object

equivalent to Cps.identity.inject(…)



102
103
104
# File 'lib/mercury/cps.rb', line 102

def self.inject(xs, &block)
  Cps.identity.inject(xs, &block)
end

.lift(&p) ⇒ Object

Returns a Cps for a non-CPS proc.



54
55
56
57
58
59
60
61
62
63
# File 'lib/mercury/cps.rb', line 54

def self.lift(&p)
  new do |*args, &k|
    value = p.call(*args)
    if value.is_a?(Cps)
      # This is technically valid, but 99% of the time it indicates a programming error.
      raise "'lift' block returned a Cps object. Did you want 'and_then'? at #{p.source_location}"
    end
    k.call(value)
  end
end

.seq(&block) ⇒ Object

Syntactic sugar for and_then chains.



5
6
7
8
9
# File 'lib/mercury/cps/seq.rb', line 5

def self.seq(&block)
  s = Seq.new
  block.call(s.method(:chain))
  s.m
end

.seql(&block) ⇒ Object

Syntactic sugar for and_then chains.



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/mercury/cps/seq_with_let.rb', line 4

def self.seql(&block)
  # EXPERIMENTAL
  # The trick here is to execute the block in a context where
  # 1. we can simulate local let-bound variables, and
  # 2. the block can access variables and methods available
  #    outside the call to seql.
  #
  # To achieve this, we instance_exec the block in a SeqWithLet
  # object, which provides the let bound variables (as methods)
  # and uses method_missing to proxy other methods to the parent
  # binding.
  #
  # Note: parent instance variables are not available inside the block.
  # Note: keyword arguments are not proxied to methods called in the parent binding
  context = SeqWithLet.new(block.binding)
  context.instance_exec(&block)
  context.__chain
end

Instance Method Details

#and_lift(&p) ⇒ Object

equivalent to: and_then { lift { … } }



47
48
49
50
51
# File 'lib/mercury/cps.rb', line 47

def and_lift(&p)
  and_then do |*args|
    Cps.lift { p.call(*args) }
  end
end

#and_then(&pm) ⇒ Object

The “bind” operation; composes two Cps

Parameters:

  • pm (Proc)

    a proc that takes the output of this Cps and returns a Cps



36
37
38
39
40
41
42
43
44
# File 'lib/mercury/cps.rb', line 36

def and_then(&pm)
  Cps.new do |*args, &k|
    self.run(*args) do |*args2|
      next_cps = pm.call(*args2)
      next_cps.is_a?(Cps) or raise "'and_then' block did not return a Cps object. Did you want 'and_lift'? at #{pm.source_location}"
      next_cps.run(&k)
    end
  end
end

#inject(xs) {|x, *args| ... } ⇒ Object

Calls and_then for each x.

Yield Parameters:

  • x (Object)

    An item from xs

  • *args (Objects)

    The value(s) passed from the last action

Yield Returns:

  • (Cps)

    The next action to add to the chain



95
96
97
98
99
# File 'lib/mercury/cps.rb', line 95

def inject(xs, &block)
  xs.inject(self) do |chain, x|
    chain.and_then { |*args| block.call(x, *args) }
  end
end

#run(*args, &k) ⇒ Object

Applies the wrapped proc. If the CPS return value is not needed, the continuation k may be omitted. Returns the return value of the continuation.



28
29
30
31
# File 'lib/mercury/cps.rb', line 28

def run(*args, &k)
  k ||= proc { |x| x }
  cps.call(*args, &k)
end