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 }


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



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



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



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.



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)


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.



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.



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

def complete? = promise.complete?

#discardTask

Maps a successful result to Unit, effectively discards it



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

def discard = fmap { Unit }

#fmapTask

Lifts a block over the Task monad.



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

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

#monadClass



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.



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.



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.



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.



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

def to_monad = self

#to_resultResult

Converts to Result. Blocks the current thread if required.



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



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.



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.



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.



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

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