How to resolve the algorithm Monads/List monad step by step in the Ruby programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Monads/List monad step by step in the Ruby programming language

Table of Contents

Problem Statement

A Monad is a combination of a data-type with two helper functions written for that type. The data-type can be of any kind which can contain values of some other type – common examples are lists, records, sum-types, even functions or IO streams. The two special functions, mathematically known as eta and mu, but usually given more expressive names like 'pure', 'return', or 'yield' and 'bind', abstract away some boilerplate needed for pipe-lining or enchaining sequences of computations on values held in the containing data-type. The bind operator in the List monad enchains computations which return their values wrapped in lists. One application of this is the representation of indeterminacy, with returned lists representing a set of possible values. An empty list can be returned to express incomputability, or computational failure. A sequence of two list monad computations (enchained with the use of bind) can be understood as the computation of a cartesian product. The natural implementation of bind for the List monad is a composition of concat and map, which, used with a function which returns its value as a (possibly empty) list, provides for filtering in addition to transformation or mapping.

Demonstrate in your programming language the following:

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Monads/List monad step by step in the Ruby programming language

Monads in Ruby

Monads are a programming construct that allows for sequencing operations in a controlled manner, handling errors and values gracefully.

Extension of the Array Class

The code begins by extending the Array class with three methods:

  • bind: Flattens an array by applying a function to each element and then flattening the result.
  • unit: Creates a new array with the given arguments.
  • lift: Converts a normal function into a monadic version that takes and returns arrays.

Lifting Functions

The lift method allows you to convert ordinary functions into monadic functions. It wraps the given function in a lambda that takes an argument and returns an array containing the function's result.

Example Usage

  • listy_inc: A monadic version of the inc function that adds 1 to each element in an array.
  • listy_str: A monadic version of the str function that converts each element in an array to a string.

Chaining Operations with bind

The bind method allows you to chain multiple operations together. Each operation takes an input array, applies a function to each element, and returns a new array.

  • Array.unit(3,4,5).bind(listy_inc).bind(listy_str): This sequence of operations adds 1 to each number, then converts each result to a string. The result is ["4", "5", "6"].

Direct Composition vs. Composition with bind

Direct composition of two monadic functions will fail in Ruby due to type signature mismatches. For example, listy_inc and listy_doub cannot be composed directly.

To compose monadic functions correctly, you can use the bind_comp method. This method chains the operations together using bind, ensuring compatibility.

  • [3,4,5].bind_comp(listy_doub, listy_inc): This sequence of operations first doubles each number, then adds 1 to each result. The result is [8, 10, 12].

Source code in the ruby programming language

class Array
  def bind(f)
    flat_map(&f)
  end
  def self.unit(*args)
    args
  end
  # implementing lift is optional, but is a great helper method for turning
  # ordinary funcitons into monadic versions of them.
  def self.lift(f)
    -> e { self.unit(f[e]) }
  end
end

inc = -> n { n + 1 }
str = -> n { n.to_s }
listy_inc = Array.lift(inc)
listy_str = Array.lift(str)

Array.unit(3,4,5).bind(listy_inc).bind(listy_str) #=> ["4", "5", "6"]

# Note that listy_inc and listy_str cannot be composed directly,
# as they don't have compatible type signature.
# Due to duck typing (Ruby will happily turn arrays into strings),
#   in order to show this, a new function will have to be used:

doub = -> n { 2*n }
listy_doub = Array.lift(doub)
[3,4,5].bind(listy_inc).bind(listy_doub) #=> [8, 10, 12]

# Direct composition will cause a TypeError, as Ruby cannot evaluate 2*[4, 5, 6]
# Using bind with the composition is *supposed* to fail, no matter the programming language.
comp = -> f, g {-> x {f[g[x]]}}
[3,4,5].bind(comp[listy_doub, listy_inc]) #=> TypeError: Array can't be coerced into Fixnum

# Composition needs to be defined in terms of bind
class Array
  def bind_comp(f, g)
    bind(g).bind(f)
  end
end

[3,4,5].bind_comp(listy_doub, listy_inc) #=> [8, 10, 12]


  

You may also check:How to resolve the algorithm Averages/Simple moving average step by step in the Python programming language
You may also check:How to resolve the algorithm Dot product step by step in the Scala programming language
You may also check:How to resolve the algorithm Lah numbers step by step in the Haskell programming language
You may also check:How to resolve the algorithm Bitwise IO step by step in the Rust programming language
You may also check:How to resolve the algorithm Topic variable step by step in the Oforth programming language