class Mercury::Cps

Attributes

cps[R]

Private Class Methods

concurrently(*cpss) click to toggle source

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.

# 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
identity() click to toggle source

The identity function as a Cps.

# File lib/mercury/cps.rb, line 66
def self.identity
  new { |*args, &k| k.call(*args) }
end
inject(xs, &block) click to toggle source

equivalent to Cps.identity.inject(…)

# File lib/mercury/cps.rb, line 102
def self.inject(xs, &block)
  Cps.identity.inject(xs, &block)
end
lift(&p) click to toggle source

Returns a Cps for a non-CPS proc.

# 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
new(&cps) click to toggle source

@param [Proc] cps a CPS proc (signature: *args, &k)

# File lib/mercury/cps.rb, line 21
def initialize(&cps)
  @cps = cps
end
seq(&block) click to toggle source

Syntactic sugar for and_then chains.

# 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) click to toggle source

Syntactic sugar for and_then chains.

# 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

Private Instance Methods

and_lift(&p) click to toggle source

equivalent to: and_then { lift { … } }

# 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) click to toggle source

The “bind” operation; composes two Cps @param [Proc] pm a proc that takes the output of this

Cps and returns a Cps
# 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, &block) click to toggle source

Calls and_then for each x. @yieldparam x [Object] An item from xs @yieldparam *args [Objects] The value(s) passed from the last action @yieldreturn [Cps] The next action to add to the chain

# 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) click to toggle source

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.

# File lib/mercury/cps.rb, line 28
def run(*args, &k)
  k ||= proc { |x| x }
  cps.call(*args, &k)
end