class Mercury::Cps
Attributes
Private Class Methods
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
The identity function as a Cps
.
# File lib/mercury/cps.rb, line 66 def self.identity new { |*args, &k| k.call(*args) } end
equivalent to Cps.identity
.inject(…)
# File lib/mercury/cps.rb, line 102 def self.inject(xs, &block) Cps.identity.inject(xs, &block) end
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
@param [Proc] cps a CPS proc (signature: *args, &k)
# File lib/mercury/cps.rb, line 21 def initialize(&cps) @cps = cps end
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
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
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
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
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
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