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.



87
88
89
# File 'lib/dry/monads/task.rb', line 87

def initialize(promise)
  @promise = promise
end

Class Method Details

.[](executor) ⇒ 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, &)
  new(Promise.execute(executor: executor, &))
end

.failed(exc) ⇒ Task

Returns a failed task from the given exception

Parameters:

  • exc (Exception)

Returns:



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

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

.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, &)
  if promise
    super(promise)
  else
    super(Promise.execute(&))
  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)


191
192
193
194
195
196
# File 'lib/dry/monads/task.rb', line 191

def ==(other)
  return true if equal?(other)
  return false unless self.class == other.class

  compare_promises(promise, other.promise)
end

#apply(val = Undefined) ⇒ 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:



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

def apply(val = Undefined, &)
  arg = Undefined.default(val, &)
  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:



119
120
121
# File 'lib/dry/monads/task.rb', line 119

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

#complete?Boolean

Whether the computation is complete.

Returns:

  • (Boolean)


201
# File 'lib/dry/monads/task.rb', line 201

def complete? = promise.complete?

#discardTask

Maps a successful result to Unit, effectively discards it

Returns:



230
# File 'lib/dry/monads/task.rb', line 230

def discard = fmap { Unit }

#fmapTask

Lifts a block over the Task monad.

Parameters:

  • block (Proc)

Returns:



111
# File 'lib/dry/monads/task.rb', line 111

def fmap(&) = self.class.new(promise.then(&))

#monadClass

Returns:

  • (Class)


204
# File 'lib/dry/monads/task.rb', line 204

def monad = Task

#or(&block) ⇒ Object

Rescues the error with a block that returns another task.

Parameters:

  • block (Proc)

Returns:

  • (Object)


153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/dry/monads/task.rb', line 153

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_fmapTask

Tranforms the error if the computation wasn’t successful.

Parameters:

  • block (Proc)

Returns:



147
# File 'lib/dry/monads/task.rb', line 147

def or_fmap(&) = self.class.new(promise.rescue(&))

#to_maybeMaybe

Converts to Maybe. Blocks the current thread if required.

Returns:



375
376
377
378
379
380
381
# File 'lib/dry/monads/maybe.rb', line 375

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:



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

def to_monad = self

#to_resultResult

Converts to Result. Blocks the current thread if required.

Returns:



397
398
399
400
401
402
403
# File 'lib/dry/monads/result.rb', line 397

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)


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/dry/monads/task.rb', line 125

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)


98
99
100
101
102
103
104
# File 'lib/dry/monads/task.rb', line 98

def value!
  if promise.wait.fulfilled?
    promise.value
  else
    raise promise.reason
  end
end

#value_orObject

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

Parameters:

  • block (Proc)

Returns:

  • (Object)


177
# File 'lib/dry/monads/task.rb', line 177

def value_or(&) = promise.rescue(&).wait.value

#wait(timeout = nil) ⇒ Task

Blocks the current thread until the task is complete.

Returns:



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

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