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
@api private
Public Class Methods
@api private
# File lib/dry/monads/task.rb, line 89 def initialize(promise) @promise = promise end
Private Class Methods
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
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
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]
# File lib/dry/monads/task.rb, line 30 def new(promise = nil, &block) if promise super(promise) else super(Promise.execute(&block)) end end
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
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
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
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
@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
Whether the computation is complete.
@return [Boolean]
# File lib/dry/monads/task.rb, line 209 def complete? promise.complete? end
@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
Maps a successful result to Unit, effectively discards it
@return [Task]
# File lib/dry/monads/task.rb, line 244 def discard fmap { Unit } end
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
@return [Class]
# File lib/dry/monads/task.rb, line 214 def monad Task end
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
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
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
Returns self.
@return [Maybe::Some, Maybe::None
]
# File lib/dry/monads/task.rb, line 221 def to_monad self end
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
@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
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
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
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