Module: Dry::Monads Private

Extended by:
Core::Extensions, Maybe::Mixin::Constructors, Result::Mixin::Constructors, Validated::Mixin::Constructors
Includes:
Core::Constants
Defined in:
lib/dry/monads.rb,
lib/dry/monads/do.rb,
lib/dry/monads/all.rb,
lib/dry/monads/try.rb,
lib/dry/monads/lazy.rb,
lib/dry/monads/list.rb,
lib/dry/monads/task.rb,
lib/dry/monads/unit.rb,
lib/dry/monads/curry.rb,
lib/dry/monads/maybe.rb,
lib/dry/monads/do/all.rb,
lib/dry/monads/errors.rb,
lib/dry/monads/result.rb,
lib/dry/monads/version.rb,
lib/dry/monads/do/mixin.rb,
lib/dry/monads/registry.rb,
lib/dry/monads/traverse.rb,
lib/dry/monads/constants.rb,
lib/dry/monads/validated.rb,
lib/dry/monads/transformer.rb,
lib/dry/monads/result/fixed.rb,
lib/dry/monads/right_biased.rb,
lib/dry/monads/extensions/json.rb,
lib/dry/monads/conversion_stubs.rb,
lib/dry/monads/extensions/rspec.rb,
lib/dry/monads/extensions/super_diff.rb,
lib/dry/monads/extensions/pretty_print.rb

Overview

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

Common, idiomatic monads for Ruby

API:

  • private

Defined Under Namespace

Modules: ConversionStubs, Curry, Do, Extensions, JSONCoder, RSpec, RightBiased, SuperDiff, Transformer Classes: ConstructorNotAppliedError, InvalidFailureTypeError, Lazy, List, Maybe, Result, Task, Try, UnwrapError, Validated

Constant Summary collapse

Unit =

Unit is a special object you can use whenever your computations don't return any payload. Previously, if your function ran a side-effect and returned no meaningful value, you had to return things like Success(nil), Success([]), Success({}), Maybe(""), Success(true) and so forth.

You should use Unit if you wish to return an empty monad.

Examples:

with Result

Success(Unit)
Failure(Unit)

with Maybe

Maybe(Unit)
Some(Unit)

API:

  • public

::Object.new.tap do |unit|
  def unit.to_s = "Unit"
  def unit.inspect = "Unit"
  def unit.deconstruct = EMPTY_ARRAY
  unit.freeze
end
Some =

See Also:

API:

  • public

Maybe::Some
None =

See Also:

API:

  • public

Maybe::None
Success =

See Also:

API:

  • public

Result::Success
Failure =

See Also:

API:

  • public

Result::Failure
VERSION =

Gem version

API:

  • public

"1.11.0"
Traverse =

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

List of default traverse functions for types. It is implicitly used by List#traverse for making common cases easier to handle.

API:

  • private

{
  Validated => -> el { el.alt_map(to_list) }
}
Valid =

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

See Also:

API:

  • private

Validated::Valid
Invalid =

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

See Also:

API:

  • private

Validated::Invalid

Class Method Summary collapse

Methods included from Maybe::Mixin::Constructors

Maybe, None, Some

Methods included from Result::Mixin::Constructors

Failure, Success

Methods included from Validated::Mixin::Constructors

Invalid, Valid

Class Method Details

.[](*monads) ⇒ Module

Build a module with cherry-picked monads. It saves a bit of typing when you add multiple monads to one class. Not loaded monads get loaded automatically.

Examples:

require 'dry/monads'

class CreateUser
  include Dry::Monads[:result, :do]

  def initialize(repo, send_email)
    @repo = repo
    @send_email = send_email
  end

  def call(name)
    if @repo.user_exist?(name)
      Failure(:user_exists)
    else
      user = yield @repo.add_user(name)
      yield @send_email.(user)
      Success(user)
    end
  end
end

Parameters:

Returns:

API:

  • public



70
71
72
73
74
75
76
77
# File 'lib/dry/monads.rb', line 70

def self.[](*monads)
  monads.sort!
  @mixins.fetch_or_store(monads.hash) do
    monads.each { load_monad(_1) }
    mixins = monads.map { registry.fetch(_1) }
    ::Module.new { include(*mixins) }.freeze
  end
end

.included(base) ⇒ Object

API:

  • public



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

def self.included(base)
  if all_loaded?
    base.include(*constructors)
  else
    raise "Load all monads first with require 'dry/monads/all'"
  end
end

.json_coder(as_json: nil, on_load: nil, **options, &block) ⇒ JSON::Coder

Returns a coder that reads and writes monads. Unknown objects raise JSON::GeneratorError.

A JSON::Coder is frozen on creation, so you cannot add monad support to an existing coder. Build your coder here instead: provide as_json: to write your own types, and on_load: to read them back. Both will run after the monad callbacks, and both see every value, so pass through anything you do not recognize.

Monads nest inside your types and the other way around, because the generator and the parser walk the whole document and call both callbacks at every step.

as_json: takes a second argument, which is true when the object is a hash key, and false everywhere else. Use it if you write a type that can be a key, because for a key you must return a String or a Symbol. Anything else raises JSON::GeneratorError:

as_json: ->(object, as_key) {
next object unless object.is_a?(Time)
as_key ? object.iso8601 : {"json_class" => "Time", "value" => object.iso8601}
}

Even if you don't need this argument, your lambda must accept it (as in ->(object, *)), or you will see an ArgumentError on the first dump.

Examples:

a coder that reads and writes monads and times

coder = Dry::Monads.json_coder(
  as_json: ->(object, *) {
    object.is_a?(Time) ? {"json_class" => "Time", "value" => object.iso8601} : object
  },
  on_load: ->(value) {
    if value.is_a?(Hash) && value["json_class"] == "Time"
      Time.iso8601(value["value"])
    else
      value
    end
  }
)

coder.dump(Some(Time.utc(2026)))
# => %({"json_class":"Dry::Monads::Maybe::Some","value":{"json_class":"Time","value":"2026-01-01T00:00:00Z"}})

Parameters:

  • (defaults to: nil)

    runs on every object the generator cannot write natively, after the monad callback

  • (defaults to: nil)

    runs on every parsed value, after the monad callback

  • passed on to JSON::Coder.new

Returns:

Raises:

API:

  • public



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/dry/monads/extensions/json.rb', line 112

def self.json_coder(as_json: nil, on_load: nil, **options, &block)
  # `JSON::Coder.new` takes its `as_json` callback as a block. We've opted to make it an keyword
  # argument for consistency alongside `on_load:`.
  #
  # A user familiar with `JSON::Coder` may provide a block, so raise an error just in case.
  raise ArgumentError, "pass the as_json callback as `as_json:`, not as a block" if block

  ::JSON::Coder.new(
    on_load: on_load ? JSONCoder::ON_LOAD >> on_load : JSONCoder::ON_LOAD,
    **options
  ) do |object, as_key|
    json = JSONCoder::AS_JSON.call(object, as_key)
    as_json ? as_json.call(json, as_key) : json
  end
end

.loaderObject

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.

API:

  • private



17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/dry/monads.rb', line 17

def self.loader
  @loader ||= ::Zeitwerk::Loader.new.tap do |loader|
    root = ::File.expand_path("..", __dir__)
    loader.tag = "dry-monads"
    loader.inflector = ::Zeitwerk::GemInflector.new("#{root}/dry-monads.rb")
    loader.push_dir(root)
    loader.ignore(
      "#{root}/dry-monads.rb",
      "#{root}/dry/monads/{all,constants,errors,registry,version}.rb",
      "#{root}/dry/monads/extensions.rb",
      "#{root}/dry/monads/extensions/**/*.rb"
    )
  end
end

.Result(error, **options) ⇒ Module

Creates a module that has two methods: Success and Failure. Success is identical to Dry::Monads::Result::Mixin::Constructors#Success and Failure rejects values that don't conform the value of the error parameter. This is essentially a Result type with the Failure part fixed.

Examples:

using dry-types

module Types
  include Dry::Types.module
end

class Operation
  # :user_not_found and :account_not_found are the only
  # values allowed as failure results
  Error =
    Types.Value(:user_not_found) |
    Types.Value(:account_not_found)

  include Dry::Monads::Result(Error)

  def (id)
     = acount_repo.find(id)

     ? Success() : Failure(:account_not_found)
  end

  def find_user(id)
    # ...
  end
end

Parameters:

  • the type of allowed failures

Returns:

API:

  • public



363
364
365
# File 'lib/dry/monads/result.rb', line 363

def self.Result(error, **options)
  Result::Fixed[error, **options]
end