Class: DeferrableGratification::Combinators::Bind

Inherits:
DefaultDeferrable show all
Defined in:
lib/deferrable_gratification/combinators/bind.rb

Overview

Combinator that passes the result of one deferred operation to a block that uses that result to begin another deferred operation. The compound operation itself succeeds if the second operation does.

You probably want to call #bind! rather than using this class directly.

If we define Deferrable err (IO a) to be the type of a Deferrable that may perform a side effect and then either succeed with a value of type a or fail with an error of type err, then we expect the block to have type

    a -> Deferrable err (IO b)

and if so this combinator (actually setup!) is a specialisation of the monadic bind operator >>=:

    # example: database query that depends on the result of another
    Bind.setup!(DB.query('select foo from bar')) do |result|
      DB.query("select baz from quuz where name = '#{result}'")
    end

    # type signatures, in pseudo-Haskell
    (>>=) :: Deferrable err a ->
                 (a -> Deferrable err b) -> Deferrable err b
    Bind :: Deferrable err a ->
                 (a -> Deferrable err (IO b)) -> Deferrable err (IO b)

However, because Ruby doesn’t actually type-check blocks, we can’t enforce that the block really does return a second Deferrable. This therefore also supports (reasonably) arbitrary blocks. However, it’s probably clearer (though equivalent) to use #transform for this case.

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Bind) initialize(first, &block)

Prepare to bind block to first, and create the Deferrable that will represent the bind.

Does not actually set up any callbacks or errbacks: call #setup! for that.

Parameters:

  • (Deferrable) first

    operation to bind to.

  • &block

    block to run on success; should return a Deferrable.

Raises:

  • (ArgumentError)

    if called without a block.



46
47
48
49
50
51
# File 'lib/deferrable_gratification/combinators/bind.rb', line 46

def initialize(first, &block)
  @first = first

  raise ArgumentError, 'must pass a block' unless block
  @proc = block
end

Class Method Details

+ (Bind) setup!(first, &block)

Create a DeferrableGratification::Combinators::Bind and register the callbacks.

Parameters:

  • (Deferrable) first

    operation to bind to.

  • &block

    block to run on success; should return a Deferrable.

Returns:

  • (Bind)

    Deferrable representing the compound operation.

Raises:

  • (ArgumentError)

    if called without a block.



67
68
69
# File 'lib/deferrable_gratification/combinators/bind.rb', line 67

def self.setup!(first, &block)
  new(first, &block).tap(&:setup!)
end

Instance Method Details

- (Object) setup!

Register a callback on the first Deferrable to run the bound block on success, and an errback to fail this DeferrableGratification::Combinators::Bind on failure.



55
56
57
58
# File 'lib/deferrable_gratification/combinators/bind.rb', line 55

def setup!
  @first.callback {|*args| run_bound_proc(*args) }
  @first.errback {|*args| self.fail(*args) }
end