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.


80
81
82
# File 'lib/dry/monads/task.rb', line 80

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

.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:


32
33
34
35
36
37
38
# File 'lib/dry/monads/task.rb', line 32

def new(promise = nil, &block)
  if promise
    super(promise)
  else
    super(Promise.execute(&block))
  end
end

.pure(value) ⇒ Task .pure(&block) ⇒ Task

Returns a complete 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)

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

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:


224
225
226
227
# File 'lib/dry/monads/task.rb', line 224

def apply(val = Undefined)
  arg = Undefined.default(val) { yield }
  bind { |f| arg.fmap { |v| curry(f).(v) } }
end

#bind(&block) ⇒ Task Also known as: then

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:


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

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

#complete?Boolean

Whether the computation is complete.

Returns:

  • (Boolean)

197
198
199
# File 'lib/dry/monads/task.rb', line 197

def complete?
  promise.complete?
end

#discardTask

Maps a successful result to Unit, effectively discards it

Returns:


232
233
234
# File 'lib/dry/monads/task.rb', line 232

def discard
  fmap { Unit }
end

#fmap(&block) ⇒ Task

Lifts a block over the Task monad.

Parameters:

  • block (Proc)

Returns:


104
105
106
# File 'lib/dry/monads/task.rb', line 104

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

#monadClass

Returns:

  • (Class)

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

def monad
  Task
end

#or(&block) ⇒ Object

Rescues the error with a block that returns another task.

Parameters:

  • block (Proc)

Returns:

  • (Object)

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/dry/monads/task.rb', line 146

def or(&block)
  child = Promise.new(
    parent: promise,
    executor: Concurrent::ImmediateExecutor.new
  )

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

  self.class.new(child)
end

#or_fmap(&block) ⇒ Task

Tranforms the error if the computation wasn't successful.

Parameters:

  • block (Proc)

Returns:


138
139
140
# File 'lib/dry/monads/task.rb', line 138

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

#to_maybeMaybe

Converts to Maybe. Blocks the current thread if required.

Returns:


255
256
257
258
259
260
261
# File 'lib/dry/monads/maybe.rb', line 255

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
210
211
# File 'lib/dry/monads/task.rb', line 209

def to_monad
  self
end

#to_resultResult

Converts to Result. Blocks the current thread if required.

Returns:


324
325
326
327
328
329
330
# File 'lib/dry/monads/result.rb', line 324

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)

120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/dry/monads/task.rb', line 120

def to_s
  state = case promise.state
          when :fulfilled
            "value=#{ value!.inspect }"
          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)

91
92
93
94
95
96
97
# File 'lib/dry/monads/task.rb', line 91

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)

172
173
174
# File 'lib/dry/monads/task.rb', line 172

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

#wait(timeout = nil) ⇒ Task

Blocks the current thread until the task is complete.

Returns:


179
180
181
182
# File 'lib/dry/monads/task.rb', line 179

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