Martin Sulzmann
We consider the “Maybe” monad.
Monads allow for the systematic control of side effects
Computations and their results are described via types, and
side-effecting computations are build via functional composition of primitive monad functions.
-- (x/y)/z
example x y z = case (divSafe x y) of
Just res -> case (divSafe res z) of
Just res2 -> "Just " ++ show res2
Nothing -> "Nothing"
This looks ugly!
Checking the result “manually” is rather clumsy!
exampleM x y z = do res <- divSafe x y
res2 <- divSafe res z
return res2
-- "do" Syntax desugared in a sequence of "bind" (>>=) operations
-- In our case,
-- (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
exampleM2 x y z = (divSafe x y)
>>=
(\res -> divSafe res z
>>=
(\res -> return res))
import Control.Applicative
import Control.Monad
-- Function with side-effect.
-- We raise an exception if we attempt to divide by zero.
divInt :: Int -> Int -> Int
divInt _ 0 = error "can't divide by zero"
divInt n m = n `div` m
divSafe :: Int -> Int -> Maybe Int
divSafe _ 0 = Nothing
divSafe n m = Just $ n `div` m
-- Maybe is a monad, can use fail/return instead of Nothing/Just.
divSafe2 :: Int -> Int -> Maybe Int
divSafe2 _ 0 = fail ""
divSafe2 n m = return $ n `div` m
example x y z = case (divSafe x y) of
Just res -> case (divSafe res z) of
Just res2 -> "Just " ++ show res2
Nothing -> "Nothing"
exampleM x y z = do res <- divSafe x y
res2 <- divSafe res z
return res2
-- "do" Syntax desugared in a sequence of "bind" (>>=) operations
-- In our case,
-- (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
exampleM2 x y z = (divSafe x y)
>>=
(\res -> divSafe res z
>>=
(\res -> return res))
runMaybe comp = case comp of
Just x -> "The result is: " ++ show x
Nothing -> "Failure"
-- Defining our own simple 'maybe' monad
data Res a = Something a | Fail String deriving Show
instance Monad Res where
return x = Something x
p >>= f = case p of
(Fail e) -> Fail e
(Something x) -> f x
instance Applicative Res where
pure = return
x <*> y = do f <- x
a <- y
return (f a)
instance Functor Res where
fmap f x = do a <- x
return (f a)
template<typename T>
class Optional {
bool b;
T val;
public:
Optional() : b(false) {}
Optional(T v) : val(v), b(true) {}
bool isJust() { return b; }
bool isNothing() { return !b; }
T fromJust() { return val; }
template<typename S>
Optional<S> bind(function<Optional<S>(T)> f) {
if (this->isNothing()) {
return *this;
} else {
auto v = this->fromJust();
return f(v);
}
}
void print() {
if (this->isNothing()) {
cout << "\n Invalid result";
} else {
cout << "\n" << this->fromJust();
}
}
}; // Optional class
template<typename T>
Optional<T> ret(T x) {
return Optional<T>(x);
}
template<typename T>
Optional<T> fail() {
return Optional<T>();
}
“bind” is a method
“return/fail” are separate functions
Our running example “(x/y)/z” rephrased in terms of C++.
Optional<int> exampleM(int x, int y, int z) {
return divSafe(x,y).bind<int>([z](int res) -> Optional<int> {
return divSafe(res,z).bind<int>([](int res2) -> Optional<int> { return ret(res2);});});
}
Looks rather ugly (there’s no “do” notation)
Also rather clumsy as we don’t have Haskell’s powerful type inference mechanism.
template<typename T>
class Optional {
Optional<T> div(T x) {
if (this->isNothing()) {
return *this;
} else if(x == 0) {
return Optional();
} else {
auto v = this->fromJust();
return Optional(v/x);
}
}
};
/*
Monads. A concept none in functional languages such as Haskell.
Turns out, monads are also useful in imperative languages.
- Systematic control of side effects.
- We use types to describe effects.
- Effectful computations are built via function composition.
Benefits.
Systematic control of side effects.
What and where side effects take place can be easily read out of the program text.
User needs to follow the "monadic" design pattern.
Example: The Optional (aka Maybe) monad.
Side effect we wish to control:
Result is successful ("just some value") or fails ("nothing").
*/
#include <functional>
#include <stdio.h>
#include <iostream>
using namespace std;
// Haskell's Maybe rephrased in C++.
template<typename T>
class Optional {
bool b;
T val;
public:
Optional() : b(false) {}
Optional(T v) : val(v), b(true) {}
bool isJust() { return b; }
bool isNothing() { return !b; }
T fromJust() { return val; }
// function<Optional<S>(T)> represents a function
// T -> Optional<S>
template<typename S>
Optional<S> bind(function<Optional<S>(T)> f) {
if (this->isNothing()) {
return *this;
} else {
auto v = this->fromJust();
return f(v);
}
}
void print() {
if (this->isNothing()) {
cout << "\n Invalid result";
} else {
cout << "\n" << this->fromJust();
}
}
// Assumes type T supports division.
Optional<T> div(T x) {
if (this->isNothing()) {
return *this;
} else if(x == 0) {
return Optional();
} else {
auto v = this->fromJust();
return Optional(v/x);
}
}
}; // Optional class
template<typename T>
Optional<T> ret(T x) {
return Optional<T>(x);
}
template<typename T>
Optional<T> fail() {
return Optional<T>();
}
// Fail safe integer division
Optional<int> divSafe(int x, int y) {
if (y == 0) {
return Optional<int>();
} else {
return Optional<int>(x / y);
}
}
// (x/y)/z
Optional<int> example(int x, int y, int z) {
auto res = divSafe(x,y);
if (res.isNothing()) {
cout << "\n Invalid result";
}
auto res2 = divSafe(res.fromJust(),z);
return res2;
}
// Optional is a monad.
// Use functional composition.
Optional<int> exampleM(int x, int y, int z) {
return divSafe(x,y).bind<int>([z](int res) -> Optional<int> {
return divSafe(res,z).bind<int>([](int res2) -> Optional<int> { return ret(res2);});});
}
// Method chaining instead of functional composition.
Optional<int> exampleM2(int x, int y, int z) {
return Optional<int>(x).div(y).div(z);
}
int main() {
int x,y,z;
x = 12; y = 2; z = 3;
example(x,y,z).print();
exampleM(x,y,z).print();
exampleM2(x,y,z).print();
}