References:
Monad: a software design pattern.
In functional programming, a monad is a software design pattern with a structure that combines program fragments (functions) and wraps their return values in a type with additional computation.
General purpose languages use monads to reduce [boilerplate code] needed for common operations (such as dealing undefined values of fallible functions, or encapsulating bookkeeping code.)
Functional languages use monads to turn complicated sequences of functions into succint pipelines that abstract away control flow, and side-effects.
Boilerplate code: In computer programming, boilerplate code, or simply boilerplate, are sections of code that are repeated in multiple places with little to no variation. When using languages that are considered verbose, the programmer must write a lot of boilerplate code to accomplish only minior functionality.
All about monads: https://wiki.haskell.org/All_About_Monads
a monadic function is a function with a single argument, written to its right. cite
A short and simple answer: a monadic function is one whose return data signature/type is the same as its input data signature/type. cite
Monad – Computation Builder: monad is a pattern for chaining operations. It looks a bit like method chaining in object-oriented languages, but the mechanism is slightly different. The pattern can be used in any language which support higher-order functions (that is, functions which can take other functions as arguments). cite
For those comming from language where the semicolon is a statement separator in imperative control flow, the metaphor of “Programmable Semicolon” has helped many understand the advantages of monads. The monad determines how combined computations form a new computation and frees the programmer from having to code the combination manually each time it is required. Think of monads as “statically typed filters” (in the Unix sense of “pipes and filters”) and you may be halfway there.
Both the concept of a monad and the term originally come from
category theory
, where a monad is defined as a [functor] with additional structure.
functor: In functional programm, a functor is a design pattern inspired by the definition from category theory that allows one to apply a function inside a generic type without changing the structure of the generic type.
More exactly, a monad can be used where unrestricted access to a value is inappropriate for reasons specific to the scenario.
In case of Maybe
monad, it is because the value may not exist.
In case of IO monad, it is because the value may not be known yet, such as when the monad represents user input that will only be provided after a prompt is displayed.
In all cases the scenarios ???
In Haskell, monads play a central role in the I/O system. It is not essential to understand monads to do I/O in the Haskell, but understanding the I/O monad will improve your code and extend your capabilities.
Monadic type + Unit operation + Bind operation (Monadic functions).
Monadic Type: A type (Maybe
)
Unit Operation: A type converter (Just (x)
)
Bind Operation: A combinator for monadic functions (>>=
or .map()
)
Example: the Maybe
type.
//
enum Maybe<T> {
Just(T),
Nothing,
}
// return nothing when the function fails.
fn divide(x: Decimal, y: Decimal) -> Maybe<Decimal> {
if y == 0 { return Nothing }
else { return Just(x / y) }
}
// divide(1.0, 4.0) -> returns Just(0.25)
// divide(3.0, 0.0) -> returns Nothing
// use if to test whether Maybe has a value.
let m_x = divide(3.14, 0.0); // see divide function above
// The if statement extracts x from m_x if m_x is the Just variant of Maybe
if let Just(x) = m_x {
print!("answer: ", x)
} else {
print!("division failed, divide by zero error...")
}
// use pattern matching
let result = divide(3.0, 2.0);
match result {
Just(x) => print!("answer: ", x),
Nothing => print!("division failed, we'll get em next time."),
}
// Use Monads to compose functions that returns `Maybe` together:
fn chainable_division(maybe_x: Maybe<Decimal>, maybe_y: Maybe<Decimal>) -> Maybe<Decimal> {
match (maybe_x, maybe_y) {
(Just(x), Just(y)) => { // If both inputs are Just, check for division by zero and divide accordingly
if y == 0 { return Nothing }
else { return Just(x / y) }
},
_ => return Nothing // Otherwise return Nothing
}
}
chainable_division(chainable_division(Just(2.0), Just(0.0)), Just(1.0)); // inside chainable_division fails, outside chainable_division returns Nothing
// Use a `bind` operator (also known as `map`, `flatmap`, or `shove`), to remove a lot of boilerplate (all those `Just` expressions)
// Rust example using ".map". maybe_x is passed through 2 functions that return Maybe<Decimal> and Maybe<String> respectively.
// As with normal function composition the inputs and outputs of functions feeding into each other should match wrapped types. (i.e. the add_one function should return a Maybe<Decimal> which then can be unwrapped to a Decimal for the decimal_to_string function)
let maybe_x: Maybe<Decimal> = Just(1.0)
let maybe_result = maybe_x.map(|x| add_one(x)).map(|x| decimal_to_string(x))
-- Maybe is a type constructor; Nothing and Just are data constructors.
data Maybe a = Nothing | Just a
-- Just is a data constructor. You can construct a data value by applying the Just data constructor to a value:
country = Just "China"
-- Maybe is a type constructor. You can construct a type by applying the Maybe type constructor to a type:
lookupAage :: DB -> String -> Maybe Int
Type Constructors – Type Polymorphic – Type Container:
Polymorphic types are like containers that are capable of holding values of many different types.
So Maybe Int
can be thought of as a Maybe
container holding an Int
value (or Nothing
) and Maybe String
would be a Maybe
container holding a String
value (or Nothing
).
Make Type of Type Containers Polymorphic:
Use m a
to represent a container of some type (m) holding a value of some type (a) !
halve :: Int -> Maybe Int
halve x
| even x = Just (x `div` 2)
| odd x = Nothing
-- This code halves x twice. it evaluates to Nothing if x is not a multiple of 4
halve x >>= halve
-- ???
chainable_division(mx,my) = mx >>= ( λx -> my >>= (λy -> Just (x / y)) )
A pattern that used many times – abstract out as monad
m >>= f = case m of
Nothing -> Nothing
Just x -> f x
eval :: Expr -> Maybe Int
eval (Val n) = return n
eval (Div x y) = do n <- eval x
m <- eval y
safediv n m
The Maybe
monad:
return :: a -> Maybe a
>>= :: Maybe a -> (a -> Maybe b) -> Maybe b
Points:
Reference: Haskell for Imperative Programmers #15 – IO IO actions.
input might be zero. Output something.
??? -----> IO ----> ()
|
\|/
environment
Monadic type + Monadic functions.
Monadic type.
Monadic function:
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?