Class: Dry::Monads::Task

Inherits:
Object
  • Object
show all
Defined in:
lib/dry/monads/task.rb,
lib/dry/monads/maybe.rb,
lib/dry/monads/result.rb

Overview

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.

Direct Known Subclasses

Lazy

Defined Under Namespace

Modules: Mixin

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(promise) ⇒ Task

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Task.



89
90
91
# File 'lib/dry/monads/task.rb', line 89

def initialize(promise)
  @promise = promise
end

Class Method Details

.[](executor, &block) ⇒ Task

Creates a Task with the given executor

Examples:

providing an executor instance, using Ruby 2.5+ syntax

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

using a predefined executor

Task[:fast] { do_quick_task }

Parameters:

  • executor (Concurrent::AbstractExecutorService, Symbol)

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

Returns:



53
54
55
# File 'lib/dry/monads/task.rb', line 53

def [](executor, &block)
  new(Promise.execute(executor: executor, &block))
end

.failed(exc) ⇒ Task

Returns a failed task from the given exception

Parameters:

  • exc (Exception)

Returns:



76
77
78
# File 'lib/dry/monads/task.rb', line 76

def failed(exc)
  new(Promise.reject(exc))
end

.new(promise) ⇒ Task .new(&block) ⇒ Task

Creates a Task from a block

Overloads:

  • .new(promise) ⇒ Task

    Parameters:

    • promise (Promise)

    Returns:

  • .new(&block) ⇒ Task

    Parameters:

    • block (Proc)

      a task to run

    Returns:



30
31
32
33
34
35
36
# 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) ⇒ Task .pure(&block) ⇒ Task

Returns a completed task from the given value

Overloads:

  • .pure(value) ⇒ Task

    Parameters:

    • value (Object)

    Returns:

  • .pure(&block) ⇒ Task

    Parameters:

    • block (Proc)

    Returns:



67
68
69
70
# File 'lib/dry/monads/task.rb', line 67

def pure(value = Undefined, &block)
  v = Undefined.default(value, block)
  new(Promise.fulfill(v))
end

Instance Method Details

#==(other) ⇒ Boolean

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

Returns:

  • (Boolean)


199
200
201
202
203
204
# 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) ⇒ Task

Applies the stored value to the given argument.

Examples:

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

Parameters:

  • val (Task) (defaults to: Undefined)

Returns:



236
237
238
239
# 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) ⇒ Task

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

Parameters:

  • block (Proc)

    A block that yields the result of the current task and returns another task

Returns:



123
124
125
# File 'lib/dry/monads/task.rb', line 123

def bind(&block)
  self.class.new(promise.flat_map { block.(_1).promise })
end

#complete?Boolean

Whether the computation is complete.

Returns:

  • (Boolean)


209
210
211
# File 'lib/dry/monads/task.rb', line 209

def complete?
  promise.complete?
end

#discardTask

Maps a successful result to Unit, effectively discards it

Returns:



244
245
246
# File 'lib/dry/monads/task.rb', line 244

def discard
  fmap { Unit }
end

#fmap(&block) ⇒ Task

Lifts a block over the Task monad.

Parameters:

  • block (Proc)

Returns:



113
114
115
# File 'lib/dry/monads/task.rb', line 113

def fmap(&block)
  self.class.new(promise.then(&block))
end

#monadClass

Returns:

  • (Class)


214
215
216
# File 'lib/dry/monads/task.rb', line 214

def monad
  Task
end

#or(&block) ⇒ Object

Rescues the error with a block that returns another task.

Parameters:

  • block (Proc)

Returns:

  • (Object)


159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# 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) ⇒ Task

Tranforms the error if the computation wasn’t successful.

Parameters:

  • block (Proc)

Returns:



151
152
153
# File 'lib/dry/monads/task.rb', line 151

def or_fmap(&block)
  self.class.new(promise.rescue(&block))
end

#to_maybeMaybe

Converts to Maybe. Blocks the current thread if required.

Returns:



406
407
408
409
410
411
412
# 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_monadMaybe::Some, Maybe::None

Returns self.

Returns:



221
222
223
# File 'lib/dry/monads/task.rb', line 221

def to_monad
  self
end

#to_resultResult

Converts to Result. Blocks the current thread if required.

Returns:



433
434
435
436
437
438
439
# 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_sString Also known as: inspect

Returns:

  • (String)


129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# 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

#value!Object

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.

Returns:

  • (Object)


100
101
102
103
104
105
106
# 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) ⇒ Object

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

Parameters:

  • block (Proc)

Returns:

  • (Object)


183
184
185
# File 'lib/dry/monads/task.rb', line 183

def value_or(&block)
  promise.rescue(&block).wait.value
end

#wait(timeout = nil) ⇒ Task

Blocks the current thread until the task is complete.

Returns:



190
191
192
193
# File 'lib/dry/monads/task.rb', line 190

def wait(timeout = nil)
  promise.wait(timeout)
  self
end