class Dry::Monads::Task

The Task monad represents an async computation. The implementation is a rather thin wrapper of Concurrent::Promise from the concurrent-ruby. The API supports setting a custom executor from concurrent-ruby.

@api public

Attributes

promise[R]

@api private

Public Class Methods

new(promise) click to toggle source

@api private

# File lib/dry/monads/task.rb, line 89
def initialize(promise)
  @promise = promise
end

Private Class Methods

[](executor, &block) click to toggle source

Creates a Task with the given executor

@example providing an executor instance, using Ruby 2.5+ syntax

IO = Concurrent::ThreadPoolExecutor.new
Task[IO] { do_http_request }

@example using a predefined executor

Task[:fast] { do_quick_task }

@param executor [Concurrent::AbstractExecutorService,Symbol]

Either an executor instance
or a name of predefined global
from concurrent-ruby

@return [Task]

# File lib/dry/monads/task.rb, line 53
def [](executor, &block)
  new(Promise.execute(executor: executor, &block))
end
failed(exc) click to toggle source

Returns a failed task from the given exception

@param exc [Exception] @return [Task]

# File lib/dry/monads/task.rb, line 76
def failed(exc)
  new(Promise.reject(exc))
end
new(promise = nil, &block) click to toggle source

Creates a Task from a block

@overload new(promise)

@param promise [Promise]
@return [Task]

@overload new(&block)

@param block [Proc] a task to run
@return [Task]
Calls superclass method
# File lib/dry/monads/task.rb, line 30
def new(promise = nil, &block)
  if promise
    super(promise)
  else
    super(Promise.execute(&block))
  end
end
pure(value = Undefined, &block) click to toggle source

Returns a completed task from the given value

@overload pure(value)

@param value [Object]
@return [Task]

@overload pure(&block)

@param block [Proc]
@return [Task]
# File lib/dry/monads/task.rb, line 67
def pure(value = Undefined, &block)
  v = Undefined.default(value, block)
  new(Promise.fulfill(v))
end

Private Instance Methods

==(other) click to toggle source

Compares two tasks. Note, it works good enough only for complete tasks.

@return [Boolean]

# File lib/dry/monads/task.rb, line 199
def ==(other)
  return true if equal?(other)
  return false unless self.class == other.class

  compare_promises(promise, other.promise)
end
apply(val = Undefined, &block) click to toggle source

Applies the stored value to the given argument.

@example

Task.
  pure { |x, y| x ** y }.
  apply(Task { 2 }).
  apply(Task { 3 }).
  to_maybe # => Some(8)

@param val [Task] @return [Task]

# File lib/dry/monads/task.rb, line 236
def apply(val = Undefined, &block)
  arg = Undefined.default(val, &block)
  bind { |f| arg.fmap { curry(f).(_1) } }
end
bind(&block) click to toggle source

Composes two tasks to run one after another. A more common name is ‘then` exists as an alias.

@param block [Proc] A block that yields the result of the current task

and returns another task

@return [Task]

# File lib/dry/monads/task.rb, line 123
def bind(&block)
  self.class.new(promise.flat_map { block.(_1).promise })
end
compare_promises(x, y) click to toggle source

@api private

# File lib/dry/monads/task.rb, line 265
def compare_promises(x, y)
  x.equal?(y) ||
    (x.fulfilled? && y.fulfilled? && x.value == y.value) ||
    (x.rejected? && y.rejected? && x.reason == y.reason)
end
complete?() click to toggle source

Whether the computation is complete.

@return [Boolean]

# File lib/dry/monads/task.rb, line 209
def complete?
  promise.complete?
end
curry(value) click to toggle source

@api private

# File lib/dry/monads/task.rb, line 251
def curry(value)
  if defined?(@curried)
    if @curried[0].equal?(value)
      @curried[1]
    else
      Curry.(value)
    end
  else
    @curried = [value, Curry.(value)]
    @curried[1]
  end
end
discard() click to toggle source

Maps a successful result to Unit, effectively discards it

@return [Task]

# File lib/dry/monads/task.rb, line 244
def discard
  fmap { Unit }
end
fmap(&block) click to toggle source

Lifts a block over the Task monad.

@param block [Proc] @return [Task] @api public

# File lib/dry/monads/task.rb, line 113
def fmap(&block)
  self.class.new(promise.then(&block))
end
inspect()
Alias for: to_s
monad() click to toggle source

@return [Class]

# File lib/dry/monads/task.rb, line 214
def monad
  Task
end
or(&block) click to toggle source

Rescues the error with a block that returns another task.

@param block [Proc] @return [Object]

# File lib/dry/monads/task.rb, line 159
def or(&block)
  child = Promise.new(
    parent: promise,
    executor: Concurrent::ImmediateExecutor.new
  )

  promise.on_error do |v|
    inner = block.(v).promise
    inner.execute
    inner.on_success { child.on_fulfill(_1) }
    inner.on_error { child.on_reject(_1) }
  rescue StandardError => e
    child.on_reject(e)
  end
  promise.on_success { child.on_fulfill(_1) }

  self.class.new(child)
end
or_fmap(&block) click to toggle source

Tranforms the error if the computation wasn’t successful.

@param block [Proc] @return [Task]

# File lib/dry/monads/task.rb, line 151
def or_fmap(&block)
  self.class.new(promise.rescue(&block))
end
to_maybe() click to toggle source

Converts to Maybe. Blocks the current thread if required.

@return [Maybe]

# File lib/dry/monads/maybe.rb, line 406
def to_maybe
  if promise.wait.fulfilled?
    Maybe::Some.new(promise.value)
  else
    Maybe::None.new(RightBiased::Left.trace_caller)
  end
end
to_monad() click to toggle source

Returns self.

@return [Maybe::Some, Maybe::None]

# File lib/dry/monads/task.rb, line 221
def to_monad
  self
end
to_result() click to toggle source

Converts to Result. Blocks the current thread if required.

@return [Result]

# File lib/dry/monads/result.rb, line 433
def to_result
  if promise.wait.fulfilled?
    Result::Success.new(promise.value)
  else
    Result::Failure.new(promise.reason, RightBiased::Left.trace_caller)
  end
end
to_s() click to toggle source

@return [String]

# File lib/dry/monads/task.rb, line 129
def to_s
  state = case promise.state
          when :fulfilled
            if Unit.equal?(value!)
              "value=()"
            else
              "value=#{value!.inspect}"
            end
          when :rejected
            "error=#{promise.reason.inspect}"
          else
            "?"
          end

  "Task(#{state})"
end
Also aliased as: inspect
value!() click to toggle source

Retrieves the value of the computation. Blocks current thread if the underlying promise hasn’t been complete yet. Throws an error if the computation failed.

@return [Object] @api public

# File lib/dry/monads/task.rb, line 100
def value!
  if promise.wait.fulfilled?
    promise.value
  else
    raise promise.reason
  end
end
value_or(&block) click to toggle source

Extracts the resulting value if the computation was successful otherwise yields the block and returns its result.

@param block [Proc] @return [Object]

# File lib/dry/monads/task.rb, line 183
def value_or(&block)
  promise.rescue(&block).wait.value
end
wait(timeout = nil) click to toggle source

Blocks the current thread until the task is complete.

@return [Task]

# File lib/dry/monads/task.rb, line 190
def wait(timeout = nil)
  promise.wait(timeout)
  self
end