### Loading All Monads at Once Source: https://context7.com/dry-rb/dry-monads/llms.txt The `require 'dry/monads/all'` directive loads all available monads, simplifying setup when multiple monads are needed. This is useful for quick setup or when the exact monads required are not precisely known. ```ruby # Load everything at once require 'dry/monads/all' # Conversion between monads (requires both to be loaded) extend Dry::Monads[:result, :maybe] Success(:foo).to_maybe # => Some(:foo) Failure(:bar).to_maybe # => None() Some(42).to_result # => Success(42) None().to_result(:missing) # => Failure(:missing) ``` -------------------------------- ### Monadic composition with bind and fmap Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/do-notation.html.md This example demonstrates composing monadic operations using 'bind' and 'fmap', which can become verbose with multiple steps. ```ruby require 'dry/monads' class CreateAccount include Dry::Monads[:result] def call(params) validate(params).bind do |values| create_account(values[:account]).bind do |account| create_owner(account, values[:owner]).fmap do |owner| [account, owner] end end end end def validate(params) # returns Success(values) or Failure(:invalid_data) end def create_account(account_values) # returns Success(account) or Failure(:account_not_created) end def create_owner(account, owner_values) # returns Success(owner) or Failure(:owner_not_created) end end ``` -------------------------------- ### Basic Task Usage Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/task.html.md Demonstrates starting two tasks concurrently and combining their results using bind and fmap. Ensure the necessary require statement is present. ```ruby require 'dry/monads' class PullUsersWithPosts include Dry::Monads[:task] def call # Start two tasks running concurrently users = Task { fetch_users } posts = Task { fetch_posts } # Combine two tasks users.bind { |us| posts.fmap { |ps| [us, ps] } } end def fetch_users sleep 3 [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }] end def fetch_posts sleep 2 [ { id: 1, user_id: 1, name: 'Hello from John' }, { id: 2, user_id: 2, name: 'Hello from Jane' }, ] end end # PullUsersWithPosts instance pull = PullUsersWithPosts.new # Spin up two tasks task = pull.call task.fmap do |users, posts| puts "Users: #{ users.inspect }" puts "Posts: #{ posts.inspect }" end puts "----" # this will be printed before the lines above ``` -------------------------------- ### Monadic composition with Do notation Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/do-notation.html.md This example shows the same monadic composition using dry-monads' 'Do' notation, which offers a cleaner syntax. ```ruby require 'dry/monads' require 'dry/monads/do' class CreateAccount include Dry::Monads[:result] include Dry::Monads::Do.for(:call) def call(params) values = yield validate(params) account = yield create_account(values[:account]) owner = yield create_owner(account, values[:owner]) Success([account, owner]) end def validate(params) # returns Success(values) or Failure(:invalid_data) end def create_account(account_values) # returns Success(account) or Failure(:account_not_created) end def create_owner(account, owner_values) # returns Success(owner) or Failure(:owner_not_created) end end ``` -------------------------------- ### Install and Load Dry Monads Source: https://context7.com/dry-rb/dry-monads/llms.txt Add the dry-monads gem to your Gemfile and bundle. You can load specific monads or all of them at once. ```ruby # Gemfile gem 'dry-monads' # Then run: # $ bundle install # Load a specific monad require 'dry/monads' # Or load all monads at once require 'dry/monads/all' ``` -------------------------------- ### Using Result with Do for Sequential Validation Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/validated.html.md This example demonstrates sequential validation using Result and Do. If any validation fails, subsequent validations are not executed, and the process stops at the first Failure. ```ruby require 'dry/monads' class CreateAccount include Dry::Monads[:result, :do] def call(form) name = yield validate_name(form) email = yield validate_email(form) password = yield validate_password(form) user = repo.create_user( name: name, email: email, password: password ) Success(user) end def validate_name(form) # Success(name) or Failure(:invalid_name) end def validate_email(form) # Success(email) or Failure(:invalid_email) end def validate_password(form) # Success(password) or Failure(:invalid_password) end end ``` -------------------------------- ### Trace a Failure value Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/tracing-failures.html.md Demonstrates how to use the `.trace` method on a `Failure` value to get the origin of the error. The trace stores only one line of the stack. ```ruby require 'create_user' create_user = CreateUser.new create_user.() # => Failure(:no_luck) create_user.().trace # => .../create_user.rb:8:in `call' ``` -------------------------------- ### Get the head and tail of a List Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/list.html.md `head` returns the first element wrapped in a `Maybe`, while `tail` returns the rest of the list. ```ruby require 'dry/monads/list' M = Dry::Monads M::List[1, 2, 3, 4].head # => Some(1) M::List[1, 2, 3, 4].tail # => List[2, 3, 4] ``` -------------------------------- ### Add dry-monads to Gemfile Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/getting-started.html.md Add the dry-monads gem to your project's Gemfile for installation. ```ruby gem 'dry-monads' ``` -------------------------------- ### Using Validated with List for Accumulated Validation Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/validated.html.md This example shows how to use Validated with List to process all validations at once and accumulate errors. If any validation fails, the result is a Failure wrapping a List of all errors. ```ruby require 'dry/monads' class CreateAccount include Dry::Monads[:list, :result, :validated, :do] def call(form) name, email, password = yield List::Validated[ validate_name(form), validate_email(form), validate_password(form) ].traverse.to_result user = repo.create_user( name: name, email: email, password: password ) Success(user) end def validate_name(form) # Valid(name) or Invalid(:invalid_name) end def validate_email(form) # Valid(email) or Invalid(:invalid_email) end def validate_password(form) # Valid(password) or Invalid(:invalid_password) end end ``` -------------------------------- ### Accumulating Validation Errors with Validated Source: https://context7.com/dry-rb/dry-monads/llms.txt Use Validated with List::Validated and traverse to collect all validation errors instead of short-circuiting on the first failure. This example demonstrates validating account creation fields. ```ruby require 'dry/monads' class CreateAccount include Dry::Monads[:list, :result, :validated, :do] def call(form) # All three validations run regardless of individual failures name, email, password = yield List::Validated[ validate_name(form), validate_email(form), validate_password(form) ].traverse.to_result Success(User.create(name: name, email: email, password: password)) end private def validate_name(form) form[:name].present? ? Valid(form[:name]) : Invalid(:invalid_name) end def validate_email(form) form[:email].include?("@") ? Valid(form[:email]) : Invalid(:invalid_email) end def validate_password(form) form[:password].length >= 8 ? Valid(form[:password]) : Invalid(:password_too_short) end end CreateAccount.new.(name: "", email: "bad", password: "123") # => Failure(List[:invalid_name, :invalid_email, :password_too_short]) CreateAccount.new.(name: "Alice", email: "a@b.com", password: "secret123") # => Success(#) ``` -------------------------------- ### Example of Accumulated Validation Failure Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/validated.html.md This snippet illustrates the output when using Validated with List, showing how multiple validation errors are collected into a Failure containing a List of errors. ```ruby create_account.(form) # => Failure(List[:invalid_name, :invalid_email]) ``` -------------------------------- ### Result Monad for Business Logic Pipelines Source: https://context7.com/dry-rb/dry-monads/llms.txt Use the Result monad to represent operations that can succeed or fail, carrying descriptive error values. This example demonstrates chaining operations to find and update a user. ```ruby require 'dry/monads' class AssociateUser include Dry::Monads[:result] def call(user_id:, address_id:) find_user(user_id).bind do |user| find_address(address_id).fmap do |address| user.update(address_id: address.id) end end end private def find_user(id) user = User.find_by(id: id) user ? Success(user) : Failure(:user_not_found) end def find_address(id) address = Address.find_by(id: id) address ? Success(address) : Failure(:address_not_found) end end AssociateUser.new.(user_id: 1, address_id: 2) # => Success(#) if both found # => Failure(:user_not_found) # => Failure(:address_not_found) ``` -------------------------------- ### Result Monad with Type Constraints Source: https://context7.com/dry-rb/dry-monads/llms.txt Enforce type constraints on Failure values to ensure error contracts are met. This example uses dry-types to constrain errors to RangeError. ```ruby require 'dry-types' module Types include Dry.Types() end class Operation Error = Types.Instance(RangeError) include Dry::Monads::Result(Error) def call(value) case value when 0..1 then Success(:ok) when -Float::INFINITY..0, 1..Float::INFINITY Failure(RangeError.new('out of range')) else Failure(TypeError.new('wrong type')) # raises! end end end Operation.new.call(0.5) # => Success(:ok) Operation.new.call(5) # => Failure(#) Operation.new.call("x") # => Dry::Monads::InvalidFailureTypeError ``` -------------------------------- ### Task Execution with Executors Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/task.html.md Shows how to specify an executor for a Task. Predefined executors include :fast, :io, and :immediate. Custom executors can also be passed. ```ruby Task[:io] { do_http_request } ``` ```ruby Task[:fast] { cpu_intensive_computation } ``` ```ruby Task[:immediate] { unsafe_io_operation } ``` ```ruby # You can pass an executor object Task[my_executor] { ... } ``` -------------------------------- ### Pattern Matching with Classic case/when Source: https://context7.com/dry-rb/dry-monads/llms.txt Shows how to use dry-monads values with Ruby's traditional `case/when` statement, leveraging case equality for matching `Success` and `Failure` types and specific error codes. ```ruby # Classic case/when (case equality) case result when Success then [:ok, result.value!] when Failure(TimeoutError) then [:timeout] when Failure(ConnectionError) then [:net_error] when Failure then [:error, result.failure] end ``` -------------------------------- ### Transaction safety with Do notation and exceptions Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/do-notation.html.md Demonstrates how 'Do' notation, by using exceptions for control flow, can be integrated with transaction blocks to ensure atomicity. ```ruby repo.transaction do account = yield create_account(values[:account]) owner = yield create_owner(account, values[:owner]) Success[account, owner] end ``` -------------------------------- ### Task Conversions to Other Monads Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/task.html.md Shows how to convert a Task to Result or Maybe monads. Note that these conversions block the current thread. ```ruby Task[:io] { 2 }.to_result # => Success(2) ``` ```ruby Task[:io] { 1/0 }.to_result # => Failure(#) ``` ```ruby Task[:io] { 2 }.to_maybe # => Some(2) ``` ```ruby Task[:io] { 1/0 }.to_maybe # => None ``` -------------------------------- ### Using bind for chained computations Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/maybe.html.md Use bind to chain computations where each step might return nil. If any step returns None, the chain stops and returns None. Accepts procs for simpler chaining. ```ruby extend Dry::Monads[:maybe] maybe_street = Maybe(user).bind do |u| Maybe(u.address).bind do |a| Maybe(a.street) end end # If user with address exists # => Some("Street Address") # If user or address is nil # => None() # You also can pass a proc to #bind add_two = -> (x) { Maybe(x + 2) } Maybe(5).bind(add_two).bind(add_two) # => Some(9) Maybe(nil).bind(add_two).bind(add_two) # => None() ``` -------------------------------- ### Nested Structure Matching with case/when Source: https://context7.com/dry-rb/dry-monads/llms.txt Illustrates advanced pattern matching for nested monadic structures, combining `case/when` with conditions on unwrapped values like `Some` and checking specific properties of the wrapped content. ```ruby # Nested structure matching case operation_result when Success(None()) then :empty_success when Success(Some { |x| x > 10 }) then :large_value when Success(Some) then :small_value when Failure then :error end ``` -------------------------------- ### Using or for alternative values Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/maybe.html.md Provides an alternative value or block result if the Maybe is None. It's the inverse of bind. ```ruby extend Dry::Monads[:maybe] add_two = -> (x) { Maybe(x + 2) } Maybe(5).bind(add_two).or(Some(0)) # => Some(7) Maybe(nil).bind(add_two).or(Some(0)) # => Some(0) Maybe(nil).bind(add_two).or { Some(0) } # => Some(0) ``` -------------------------------- ### Accessing Unit Value Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/unit.html.md Demonstrates how to access the `Unit` value from a constructor that uses it by default. The `Unit` value is a singleton and is not `nil`. ```ruby extend Dry::Monads[:result] Success().value! # => Unit ``` -------------------------------- ### Enable multiple monads for interaction Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/getting-started.html.md Enable multiple monads, such as Result and Maybe, to allow for seamless conversion and interaction between them. ```ruby extend Dry::Monads[:result, :maybe] Success(:foo).to_maybe # => Some(:foo) ``` -------------------------------- ### Do::All with Class Methods Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/do-notation.html.md Demonstrates using `Do::All` with class methods by extending the class with `Dry::Monads[:result, :do]`. This allows `yield` to be used directly within class methods for monadic chaining. ```ruby require 'dry/monads' class SomeClassLevelLogic extend Dry::Monads[:result, :do] def self.call x = yield Success(5) y = yield Success(20) Success(x * y) end end SomeClassLevelLogic.() # => Success(100) ``` -------------------------------- ### Pattern Matching with Maybe Monad Source: https://context7.com/dry-rb/dry-monads/llms.txt Demonstrates pattern matching specifically for the `Maybe` monad using `case/in`. Handles `Some` values, including conditional checks, and `None` for missing values. ```ruby # Maybe pattern matching case Maybe(User.find_by(id: params[:id])) in Some(Integer => x) if x > 0 "Positive id: #{x}" in None "Not found" end ``` -------------------------------- ### Basic Try Usage Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/try.html.md Demonstrates basic usage of the Try monad to wrap operations that may raise exceptions. By default, it catches exceptions inherited from StandardError. You can specify particular exceptions to catch. ```ruby require 'dry/monads' class ExceptionalLand include Dry::Monads[:try] def call res = Try { 10 / 2 } res.value! if res.value? # => 5 res = Try { 10 / 0 } res.exception if res.error? # => # # By default Try catches all exceptions inherited from StandardError. # However you can catch only certain exceptions like this Try[NoMethodError, NotImplementedError] { 10 / 0 } # => raised ZeroDivisionError: divided by 0 exception end end ``` -------------------------------- ### Maybe Combinators: or, and, filter, flatten, to_result Source: https://context7.com/dry-rb/dry-monads/llms.txt Utilize combinators like `or` for fallbacks, `and` to combine two Maybes, `filter` based on a predicate, `flatten` to unwrap nested Maybes, and `to_result` for conversion. ```ruby extend Dry::Monads[:maybe] # or: fallback when None Maybe(nil).bind { |x| Maybe(x + 2) }.or(Some(0)) # => Some(0) None() | Some(1) | Some(2) # => Some(1) # and: combine two Maybe values Some(5).and(Some(10)) { |x, y| x + y } # => Some(15) Some(5).and(None()) { |x, y| x + y } # => None() Some(5).and(Some(10)) # => Some([5, 10]) # filter: keep Some only when predicate holds Some(3).filter(&:odd?) # => Some(3) Some(4).filter(&:odd?) # => None() # flatten: remove one level of Maybe nesting Some(Some(10)).flatten # => Some(10) Some(None()).flatten # => None() # to_result: convert to Result Some(10).to_result # => Success(10) None().to_result(:not_found) # => Failure(:not_found) ``` -------------------------------- ### Include multiple monads Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/getting-started.html.md Include multiple monads, such as Maybe and Result, by listing them in the mixin. ```ruby require 'dry/monads' class CreateUser include Dry::Monads[:maybe, :result] end ``` -------------------------------- ### Directly Use Do Notation with Do.() Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/do-notation.html.md Use `Dry::Monads::Do.()` to apply Do notation in any context, such as a standalone block of code. This requires explicitly binding values using `Dry::Monads::Do.bind`. ```ruby require 'dry/monads/do' require 'dry/monads/result' # some random place in your code Dry::Monads::Do.() do user = Dry::Monads::Do.bind create_user account = Dry::Monads::Do.bind create_account(user) Dry::Monads::Success[user, account] end ``` -------------------------------- ### bind Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/list.html.md Lifts a block or proc and runs it against each member of the list. The block must return a value coercible to a list. If no block is given, the first argument will be treated as callable and used instead. ```APIDOC ## bind ### Description Lifts a block/proc and runs it against each member of the list. The block must return a value coercible to a list. As in other monads if no block given the first argument will be treated as callable and used instead. ### Method Signature `bind { |element| ... }` or `bind(callable)` ### Example ```ruby require 'dry/monads/list' M = Dry::Monads M::List[1, 2].bind { |x| [x + 1] } # => List[2, 3] M::List[1, 2].bind(-> x { [x, x + 1] }) # => List[1, 2, 2, 3] M::List[1, nil].bind { |x| [x + 1] } # => error ``` ``` -------------------------------- ### head and tail Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/list.html.md Provides methods to access the first element (head) and the rest of the list (tail). ```APIDOC ## head and tail ### Description `head` returns the first element wrapped with a `Maybe`. `tail` returns the rest of the list. ### Method Signatures `head` `tail` ### Example ```ruby require 'dry/monads/list' M = Dry::Monads M::List[1, 2, 3, 4].head # => Some(1) M::List[1, 2, 3, 4].tail # => List[2, 3, 4] ``` ``` -------------------------------- ### Do Notation with Transaction Safety Source: https://context7.com/dry-rb/dry-monads/llms.txt Use Do Notation with the Result monad to automatically roll back database transactions on failure. Ensure `dry/monads` is required and the necessary monads are included. ```ruby require 'dry/monads' class TransactionalCreate include Dry::Monads[:result, :do] def call(account_params, owner_params) repo.transaction do account = yield create_account(account_params) owner = yield create_owner(account, owner_params) # If create_owner returns Failure, transaction is rolled back Success[account, owner] end end end ``` -------------------------------- ### Chaining operations with bind Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/try.html.md Use `bind` to chain operations that may raise exceptions. This allows for sequential execution where each step can potentially fail and be caught by the Try monad. ```ruby Try[NetworkError, DBError] { grap_user_by_making_request }.bind { |user| user_repo.save(user) } # Possible outcomes: # => Value(persisted_user) # => Error(NetworkError: request timeout) # => Error(DBError: unique constraint violated) ``` -------------------------------- ### Tracing Failures to Source Location Source: https://context7.com/dry-rb/dry-monads/llms.txt Every `Failure` and `None` value automatically records its creation location (file and line). Access this information using the `.trace` method to quickly debug errors without a full stack trace. ```ruby require 'dry/monads' class CreateUser include Dry::Monads[:result] def call(params) return Failure(:invalid_params) unless params[:name] Success(User.create(params)) end end result = CreateUser.new.(name: nil) result # => Failure(:invalid_params) result.trace # => ".../create_user.rb:6:in `call'" ``` -------------------------------- ### Include Do::All for Automatic Method Wrapping Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/do-notation.html.md Use `Do::All` to automatically wrap methods in a class, simplifying the chaining of monadic operations. This is useful for sequential operations within a class method. ```ruby require 'dry/monads' class CreateAccount # This will include Do::All by default include Dry::Monads[:result, :do] def call(account_params, owner_params) repo.transaction do account = yield create_account(account_params) owner = yield create_owner(account, owner_params) Success[account, owner] end end def create_account(params) values = yield validate_account(params) account = repo.create_account(values) Success(account) end def create_owner(account, params) values = yield validate_owner(params) owner = repo.create_owner(account, values) Success(owner) end def validate_account(params) # returns Success/Failure end def validate_owner(params) # returns Success/Failure end end ``` -------------------------------- ### Task Exception Handling Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/task.html.md Illustrates how Task captures exceptions, preventing them from being re-raised. Failures can be processed using .or or .or_fmap. ```ruby io_fail = Task[:io] { 1/0 } io_fail # => Task(error=#) ``` ```ruby immediate_fail = Task[:immediate] { 1/0 } immediate_fail # => Task(error=#) ``` ```ruby Task[:immediate] { 1/0 }.or { M::Task[:immediate] { 0 } } # => Task(value=0) ``` ```ruby Task[:immediate] { 1/0 }.or_fmap { 0 } # => Task(value=0) ``` -------------------------------- ### Do Notation for Composing Monadic Operations Source: https://context7.com/dry-rb/dry-monads/llms.txt Simplify nested monadic operations using Do Notation, which allows composing steps with `yield`. Failures short-circuit execution and are returned immediately. ```ruby require 'dry/monads' class CreateAccount # :do includes Do::All, wrapping all instance methods automatically include Dry::Monads[:result, :do] def call(account_params, owner_params) # yield unwraps Success or short-circuits on Failure values = yield validate(account_params) account = yield create_account(values) owner = yield create_owner(account, owner_params) Success([account, owner]) # Returns one of: # => Success([account, owner]) # => Failure(:invalid_params) # => Failure(:account_not_created) # => Failure(:owner_not_created) end private def validate(params) params[:name] ? Success(params) : Failure(:invalid_params) end def create_account(values) account = Account.create(values) account.persisted? ? Success(account) : Failure(:account_not_created) end def create_owner(account, params) owner = account.create_owner(params) owner.persisted? ? Success(owner) : Failure(:owner_not_created) end end CreateAccount.new.( { name: "Acme" }, { email: "owner@acme.com" } ) # => Success([#, #]) ``` -------------------------------- ### Default Unit Representation Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/unit.html.md Shows how `Unit` is typically represented when it's the default value for a constructor. The output omits the `Unit` value for brevity. ```ruby extend Dry::Monads[:result] # Outputs as "Success()" but technically it's "Success(Unit)" Success() ``` -------------------------------- ### Include Maybe monad constructors Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/getting-started.html.md Include the Maybe monad to gain access to its value constructors like Some(...) and None(). ```ruby require 'dry/monads' class CreateUser include Dry::Monads[:maybe] def call(params) # ... if valid?(params) Some(create_user(params)) else None() end end end ``` -------------------------------- ### Basic Case Matching with Dry-Monads Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/case-equality.html.md Use the case operator to match against specific values or ranges within a monad. ```ruby case value when Some(1), Some(2) then :one_or_two when Some(3..5) then :three_to_five else :something_else end ``` -------------------------------- ### Use Result monad with do notation Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/getting-started.html.md Utilize the Result monad with do notation for cleaner chaining of operations that can succeed or fail. ```ruby require 'dry/monads' class ResultCalculator include Dry::Monads[:result, :do] def calculate(input) value = Integer(input) value = yield add_3(value) value = yield mult_2(value) Success(value) end def add_3(value) if value > 1 Success(value + 3) else Failure("value was less than 1") end end def mult_2(value) if value % 2 == 0 Success(value * 2) else Failure("value was not even") end end end c = ResultCalculator.new c.calculate(3) # => Success(12) c.calculate(0) # => Failure("value was less than 1") c.calculate(2) # => Failure("value was not even") ``` -------------------------------- ### Require all monads Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/getting-started.html.md Require all monads at once for convenience when using a wide range of monadic features. ```ruby require 'dry/monads/all' ``` -------------------------------- ### Pattern Matching with Monadic Values (case/in) Source: https://context7.com/dry-rb/dry-monads/llms.txt Integrates dry-monads with Ruby 2.7+ pattern matching using the `case/in` syntax. This allows destructuring and conditional logic based on the Success or Failure state and their contents. ```ruby # Ruby 2.7+ pattern matching with case/in case find_user(params[:id]) in Success(Integer => id) "Found user with integer id: #{id}" in Success[:created, user] "Newly created: #{user.name}" in Success(name:, email:) "User #{name} <#{email}>" in Success() "Success with no value (Unit)" in Failure[:not_found] "User not found" in Failure[error_code, *details] "Error #{error_code}: #{details}" end ``` -------------------------------- ### Matching Specific Failures with Case Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/case-equality.html.md Match against specific error types within a Failure monad, or a generic Failure. ```ruby case value when Success then [:ok, value.value!] when Failure(TimeoutError) then [:timeout] when Failure(ConnectionClosed) then [:net_error] when Failure then [:generic_error] else raise "Unhandled case" end ``` -------------------------------- ### Case Matching Nested Structures Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/case-equality.html.md Handle nested monad structures, including None, Some with conditions, and generic Some. ```ruby case value when Success(None()) then :nothing when Success(Some { |x| x > 10 }) then :something when Success(Some) then :something_else when Failure then :error end ``` -------------------------------- ### Simplifying Result monad composition with do notation Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/index.html.md Employ `do` notation for a clear, sequential-like syntax when composing operations that return Result monads. `yield` unwraps `Success` values or short-circuits on `Failure`. ```ruby user = yield find_user(params[:user_id]) address = yield find_address(params[:address_id]) Success(user.update(address_id: address.id)) ``` -------------------------------- ### Composing operations with Maybe monad's bind Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/index.html.md Use `bind` to chain operations where each operation might return a Maybe monad. This ensures that if any step results in `None`, the subsequent operations are not executed. ```ruby maybe_user = Maybe(User.find_by(id: params[:user_id])) maybe_user.bind do |user| maybe_address = Maybe(Address.find_by(id: params[:address_id])) maybe_address.bind do |address| user.update(address_id: address.id) end end ``` -------------------------------- ### Using maybe for nil-to-None mapping Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/maybe.html.md Similar to fmap, but explicitly maps nil results from blocks to None. This mimics the behavior of Ruby's safe navigation operator (&.) but with monadic wrapping. ```ruby extend Dry::Monads[:maybe] Maybe(user).maybe(&:address).maybe(&:street) # If user with address exists # => Some("Street Address") # If user or address is nil # => None() ``` -------------------------------- ### Recovering from specific exceptions Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/try.html.md The `recover` method allows you to provide a fallback value or operation when a specific exception occurs. If the Try block succeeds, `recover` is a no-op. ```ruby extend Dry::Monads[:try] Try { 10 / 0 }.recover(ZeroDivisionError) { 1 } # => Try::Value(1) ``` ```ruby extend Dry::Monads[:try] Try { Hash.new.fetch(:missing) }.recover { :found } # => Try::Value(:found) ``` ```ruby Try { 10 }.recover { 1 } # => Try::Value(10) ``` ```ruby extend Dry::Monads[:try] Try { bang! }.recover(KeyError, ArgumentError) { :failsafe } ``` -------------------------------- ### Construct array values with Failure Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/getting-started.html.md Construct array values directly within the Failure constructor for concise error reporting. ```ruby require 'dry/monads' class CreateUser include Dry::Monads[:result] def call(params) # ... # Same as Failure([:user_exists, params: params]) Failure[:user_exists, params: params] end end ``` -------------------------------- ### Bind a block to each element in a List Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/list.html.md Lifts a block and runs it against each member of the list. The block must return a value coercible to a list. If no block is given, the first argument is treated as callable. ```ruby require 'dry/monads/list' M = Dry::Monads M::List[1, 2].bind { |x| [x + 1] } # => List[2, 3] M::List[1, 2].bind(-> x { [x, x + 1] }) # => List[1, 2, 2, 3] M::List[1, nil].bind { |x| [x + 1] } # => error ``` -------------------------------- ### Do Notation for Class Methods Source: https://context7.com/dry-rb/dry-monads/llms.txt Apply Do Notation to class-level methods using `extend Dry::Monads[:result, :do]`. This allows monadic composition within class methods. ```ruby require 'dry/monads' class Calculator extend Dry::Monads[:result, :do] def self.call x = yield Success(5) y = yield Success(20) Success(x * y) end end Calculator.() # => Success(100) ``` -------------------------------- ### Convert Result to Maybe - Result Monad Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/result.html.md Use `to_maybe` to convert a `Result` into a `Maybe` monad. A `Success` becomes `Some` and a `Failure` becomes `None`. ```ruby extend Dry::Monads[:result, :maybe] result = if foo > bar Success(10) else Failure("wrong") end.to_maybe # If everything went success result # => Some(10) # If it did not result # => None() ``` -------------------------------- ### Match List Values with dry-monads Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/pattern-matching.html.md Pattern match on List monads. Supports matching specific elements, variable-length patterns, and empty lists. ```ruby case value in List[Integer] # any list of size 1 with an integer in List[1, 2, 3, *] # list with size >= 3 starting with 1, 2, 3 in List[] # empty list end ``` -------------------------------- ### Converting Maybe to Result Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/maybe.html.md Converts a Maybe monad to a Result monad. Some becomes Success, and None becomes Failure. Failure can be customized with a value or a block. ```ruby extend Dry::Monads[:maybe, :result] Some(10).to_result # => Success(10) None().to_result # => Failure() None().to_result(:error) # => Failure(:error) None().to_result { :block_value } # => Failure(:block_value) ``` -------------------------------- ### Define a class with Failure Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/tracing-failures.html.md This snippet shows how to define a class that includes `dry-monads` and returns a `Failure` value. ```ruby require 'dry/monads' class CreateUser include Dry::Monads[:result] def call Failure(:no_luck) end end ``` -------------------------------- ### fmap Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/list.html.md Maps a block over the list, acting as Array#map. If no block is given, the first argument will be treated as callable and used instead. ```APIDOC ## fmap ### Description Maps a block over the list. Acts as `Array#map`. As in other monads, if no block given the first argument will be treated as callable and used instead. ### Method Signature `fmap { |element| ... }` or `fmap(callable)` ### Example ```ruby require 'dry/monads/list' M = Dry::Monads M::List[1, 2].fmap { |x| x + 1 } # => List[2, 3] ``` ``` -------------------------------- ### Yield for unwrapping monadic values Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/do-notation.html.md Illustrates how 'yield' unwraps a Success value or short-circuits on a Failure, simplifying monadic flow control. ```ruby account = yield create_account(values[:account]) ``` -------------------------------- ### Safe unwrapping with value_or Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/maybe.html.md Unwraps the value if Some, otherwise returns the provided default value or the result of the block. This is the recommended way to safely extract values. ```ruby extend Dry::Monads[:maybe] add_two = -> (x) { Maybe(x + 2) } Maybe(5).bind(add_two).value_or(0) # => 7 Maybe(nil).bind(add_two).value_or(0) # => 0 Maybe(nil).bind(add_two).value_or { 0 } # => 0 ``` -------------------------------- ### Result Monad Core Transformation Methods Source: https://context7.com/dry-rb/dry-monads/llms.txt Utilize fmap to transform success values, bind to chain operations that return a Result, and or to provide fallback values on failure. ```ruby extend Dry::Monads[:result] # fmap: transform success value, pass through failure Success(10).fmap { |x| x * 2 } # => Success(20) Failure("err").fmap { |x| x * 2 } # => Failure("err") Success('hello').fmap(:upcase.to_proc) # => Success("HELLO") # bind: chain operations that return Result Success(5).bind { |x| x > 0 ? Success(x * 10) : Failure("negative") } # => Success(50) # or: recover from failure Failure("error").or { |e| Failure("wrapped: #{e}") } # => Failure("wrapped: error") Success(10).or(Success(99)) # => Success(10) ``` -------------------------------- ### Simplifying Maybe monad composition with do notation Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/index.html.md Use `do` notation for a more readable, sequential-like syntax when composing operations that return Maybe monads. The `yield` keyword unwraps the monadic value or short-circuits if it's `None`. ```ruby user = yield find_user(params[:user_id]) address = yield find_address(params[:address_id]) Some(user.update(address_id: address.id)) ``` -------------------------------- ### Chaining values with and Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/maybe.html.md Chains two Maybe values. If both are Some, applies the block to their unwrapped values. If either is None, returns None. Can also combine two Some values into a tuple. ```ruby extend Dry::Monads[:maybe] Some(5).and(Some(10)) { |x, y| x + y } # => Some(15) Some(5).and(None) { |x, y| x + y } # => None() None().and(Some(10)) { |x, y| x + y } # => None() Some(5).and(Some(10)) # => Some([5, 10]) Some(5).and(None()) # => None() ``` -------------------------------- ### Composing operations with Maybe monad and external methods Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/index.html.md Chain operations that return Maybe monads, assuming helper methods like `find_user` and `find_address` already return Maybe values. ```ruby find_user(params[:user_id]).bind do |user| find_address(params[:address_id]).bind do |address| Some(user.update(address_id: address.id)) end end ``` -------------------------------- ### Using the Unit Type for Success Without Value Source: https://context7.com/dry-rb/dry-monads/llms.txt The `Unit` type is a singleton representing a successful operation with no meaningful return value. It's the default value for `Success()` and can be used to explicitly signal completion. ```ruby extend Dry::Monads[:result] # Default unit value Success().value! # => Unit Success() # prints as "Success()" # Discard a wrapped value, replacing it with Unit result = create_user # => Success(#) result.discard # => Success() (Failure is left unchanged) # Unit is truthy if Dry::Monads::Unit puts "Unit is truthy" end ``` -------------------------------- ### Defining Success and Failure with Result monad Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/index.html.md Define functions that return a Result monad, indicating either a successful outcome with a value (`Success`) or a failure with an error reason (`Failure`). This provides more context than Maybe's `None`. ```ruby def find_user(user_id) user = User.find_by(id: user_id) if user Success(user) else Failure(:user_not_found) end end def find_address(address_id) address = Address.find_by(id: address_id) if address Success(address) else Failure(:address_not_found) end end ``` -------------------------------- ### Maybe#fmap for Mapping Plain Values Source: https://context7.com/dry-rb/dry-monads/llms.txt Maps a block over a Some value, returning a new Maybe. The block can return a plain value. Passes None through unchanged. Use #maybe for nil-coercing fmap. ```ruby extend Dry::Monads[:maybe] Maybe(10).fmap { |x| x + 5 }.fmap { |y| y * 2 } # => Some(30) Maybe(nil).fmap { |x| x + 5 } # => None() # Use #maybe (alias of fmap that coerces nil returns to None) Maybe(user).maybe(&:address).maybe(&:street) # => Some("Baker Street") or None() ``` -------------------------------- ### Match Maybe Values with dry-monads Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/pattern-matching.html.md Pattern match on Maybe monads (Some and None). Supports type checks and guards for destructuring values. ```ruby case value in Some(Integer => x) if x > 0 # x is a positive integer in Some(Float | String) # ... in None # ... end ``` -------------------------------- ### traverse Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/list.html.md Traverses the list with a block (or without it), flipping the List structure with the given monad. Requires the list to be typed. ```APIDOC ## traverse ### Description Traverses the list with a block (or without it). This method "flips" List structure with the given monad (obtained from the type). **Note that traversing requires the list to be typed.** ### Method Signature `traverse` or `traverse { |element| ... }` ### Example ```ruby require 'dry/monads/list' M = Dry::Monads M::List[M::Success(1), M::Success(2)].typed(M::Result).traverse # => Success(List[1, 2]) M::List[M::Maybe(1), M::Maybe(nil), M::Maybe(3)].typed(M::Maybe).traverse # => None # also, you can use fmap with #traverse M::List[1, 2].fmap { |x| M::Success(x) }.typed(M::Result).traverse # => Success(List[1, 2]) M::List[1, nil, 3].fmap { |x| M::Maybe(x) }.typed(M::Maybe).traverse # => None ``` ``` -------------------------------- ### value Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/list.html.md Unwraps the result of the List monad to return the underlying array. ```APIDOC ## value ### Description You always can unwrap the result by calling `value`. ### Method Signature `value` ### Example ```ruby require 'dry/monads/list' M = Dry::Monads M::List[1, 2].value # => [1, 2] ``` ``` -------------------------------- ### Discarding Values with Unit Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/unit.html.md Illustrates the use of the `.discard` method to map a wrapped value to `Unit`. This is useful when the outcome of an operation is not relevant to the caller. `Failure` values are not affected. ```ruby extend Dry::Monads[:result] result = create_user # returns Success(#) or Failure(...) result.discard # => Maps Success(#) to Success() but lefts Failure(...) intact ``` -------------------------------- ### Maybe#bind for Chained Monadic Operations Source: https://context7.com/dry-rb/dry-monads/llms.txt Applies a block to the wrapped value if Some, returning a new Maybe. Passes None through unchanged. Useful for chaining operations that return Maybes. ```ruby extend Dry::Monads[:maybe] add_two = ->(x) { Maybe(x + 2) } Maybe(5).bind(add_two).bind(add_two) # => Some(9) Maybe(nil).bind(add_two).bind(add_two) # => None() # Nested bind for chained lookups Maybe(user).bind do |u| Maybe(u.address).bind do |a| Maybe(a.street) end end # => Some("123 Main St") or None() ``` -------------------------------- ### Match Result Values with dry-monads Source: https://github.com/dry-rb/dry-monads/blob/main/docsite/source/pattern-matching.html.md Use pattern matching to deconstruct Success and Failure values. Supports type checks, guards, and destructuring of nested data like hashes and arrays. ```ruby case value in Success(Integer => x) # x is bound to an integer in Success[:created, user] # user is bound to the second member in Success(Date | Time) # date or time object in Success[1, *] # any array starting with 1 in Success(String => s) if s.size < 100 # only if `s` is short enough in Success(counter: Integer) # matches Success(counter: 50) # doesn't match Success(counter: 50, extra: 50) in Success(user: User, account: Account => user_account) # matches Success(user: User.new(...), account: Account.new(...), else: ...) # user_account is bound to the value of the `:account` key in Success() # corresponds to Success(Unit) in Success(user:, **rest) # matches Success(user: User.new, other_key: "value") in Success(_) # general success in Failure[:user_not_found] # matches Failure([:user_not_found]) or Failure[:user_not_found] in Failure[error_code, *payload] # ... end ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.