Monad is the thing that lets you compose functions with multiple return values.
It has nothing to do with I/O operations except that some shills would wrap print function around monad and believe that its no longer procedural programming.
a monad is a thingy with 2 other thingies.
one thingy composes/defines a bunch of functions and the other thingy takes the outputs of these functions and creates a type out of them all.
Monads are basically functors m where nesting m(m(ma)) doesn't add complexity, ie. where multiple nesting can be flattened out in a unique way to a m(a).
The obvious example is mathematical expressions, eg. 3x+1. No matter how many times you nest, 3(2y+2)+1, 3(2(3+z)+2)+1, ..., you just get another expression that simplifies to a unique expression. Most example can be viewed as variations on this.
Another example is options. If you have an Option[Option[Option[int]]] then either you have a single int, eg. Some(Some(Some(3*~~, or it stops at None, so you can view it as just Option[int].
Same for lists, no matter how many times you nest them, [[1,2],[3],[4]], if you just read it left to right, you get a single list of numbers.
IO is a monad since if you have a program that produces a program (that produces a program that produces a program...) you can view it as just one program that executes the whole sequence at once.
Monads are something that barely anyone truly understands, it's clear from the terrible explanations everyone gives, whether they are ironic or not. If you can't explain i simply, you didn't understand it well enough.
A language built on such a vague concept is just a badly designed programming language, no amount of complexity worship is going to change this.
It's about structuring the code / function composition.
Consider this pseudocode (contrived, but that's not the point):
function ReadValueFromConfig(key, configFile):
var file = openFile(configFile)
var json = parseJson(file)
var value = json.get(key)
return value
It has no error checking. Let's assume that each function can return either a result or null:
function ReadValueFromConfig(key, configFile):
var file = openFile(configFile)
if file == null:
return null
var json = parseJson(file)
if json == null:
return null
var value = json.get(key)
if value == null
return null
return value
But now the code is littered with boilerplate.
This is the problem solved by monad (horrible name btw).
Insted of explicitly writing all those checks, you define a composition rule that says (this is not even pseudocode):
Monad "Optional": bind funcA and funcB =>
if funcA returns null, return null
otherwise, pass the result to funcB
finally, return whatever funcB returns
Then, you can use the first version of the code, but with an annotation saying that it's transformed using your monad "Optional":
function ReadValueFromConfig(key, configFile) using Optional:
var file = openFile(configFile)
var json = parseJson(file)
var value = json.get(key)
return value
In haskell the specific syntax sugar for that is called "do notation".
Some example datatypes:
data Maybe a = Just a | Nothing
newtype State s a = State { runState :: s -> (s, a) }
Functors:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Take a function, take a value which contains another value, modify that inner value. Simple.
Example:
fmap f (Just v) = Just (f v)
fmap _ Nothing = Nothing
fmap f (State sf) = State (s ->
let (s', v) = sf s
in (s', f v))
Applicatives:
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Like functor, but the function is also contained inside a wrapped value.
The wrappers need to combine together somehow, as well as map over the internal value.
pure just creates a wrapper around a value.
Example:
pure = Just
(Just f) <*> (Just v) = Just (f v)
_ <*> _ = Nothing
pure v = State (s -> (s, v))
(State sf) <*> (State sv) = State (s ->
let (s', f) = sf s
(s'', v) = sv s'
in (s'', f v))
You can also do this:
fmap :: (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = (fmap f a) <*> b
Applicatives let you run multi-parameter functions over wrapped values, while functor only lets you run single-parameter functions.
Monads:
class (Applicative m) => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
Like functor, except the mapping function returns a new wrapped value, instead of just a new value.
In effect, this means the mapping function can modify the wrapper as well as the value, which functor cannot.
return is the same as pure.
Example:
return = pure
(Just v) >>= k = k v
Nothing >>= _ = Nothing
(State sf) >>= k = State (s ->
let (s', v) = sf s
in runState (k v) s')
Contexts. Context of failure. Context of state. Context of side effects. Now you want to do something with the values which are stuck in these bubble contexts. Hence, functors, applicatives and monads
>filtered
Just go back to OOP.
you don't need to get it, Haskell is a toy language for math nerds that don't write software
you don't have to understand/know anything iToddler, just do your normalgay things like laughing at different color bubbles or browsing reddit
Monad is just a monoid the the category of endofunctors.
what's a monoid and what's an endofunctor?
It's just a functor in the terminal bicategory.
What's a terminal bicategory
It's just a category of preassociative isomorphic groupoids.
Monad is the thing that lets you compose functions with multiple return values.
It has nothing to do with I/O operations except that some shills would wrap print function around monad and believe that its no longer procedural programming.
so a monad can return multiple values at once?
>Monad is the thing that lets you compose functions with multiple return values.
How's that different from a function that returns an array with multiple values?
So a monad is basically a glorified tuple?
a monad is a thingy with 2 other thingies.
one thingy composes/defines a bunch of functions and the other thingy takes the outputs of these functions and creates a type out of them all.
Monads are basically functors m where nesting m(m(ma)) doesn't add complexity, ie. where multiple nesting can be flattened out in a unique way to a m(a).
The obvious example is mathematical expressions, eg. 3x+1. No matter how many times you nest, 3(2y+2)+1, 3(2(3+z)+2)+1, ..., you just get another expression that simplifies to a unique expression. Most example can be viewed as variations on this.
Another example is options. If you have an Option[Option[Option[int]]] then either you have a single int, eg. Some(Some(Some(3*~~, or it stops at None, so you can view it as just Option[int].
Same for lists, no matter how many times you nest them, [[1,2],[3],[4]], if you just read it left to right, you get a single list of numbers.
IO is a monad since if you have a program that produces a program (that produces a program that produces a program...) you can view it as just one program that executes the whole sequence at once.
Monads are something that barely anyone truly understands, it's clear from the terrible explanations everyone gives, whether they are ironic or not. If you can't explain i simply, you didn't understand it well enough.
A language built on such a vague concept is just a badly designed programming language, no amount of complexity worship is going to change this.
It's about structuring the code / function composition.
Consider this pseudocode (contrived, but that's not the point):
function ReadValueFromConfig(key, configFile):
var file = openFile(configFile)
var json = parseJson(file)
var value = json.get(key)
return value
It has no error checking. Let's assume that each function can return either a result or null:
function ReadValueFromConfig(key, configFile):
var file = openFile(configFile)
if file == null:
return null
var json = parseJson(file)
if json == null:
return null
var value = json.get(key)
if value == null
return null
return value
But now the code is littered with boilerplate.
This is the problem solved by monad (horrible name btw).
Insted of explicitly writing all those checks, you define a composition rule that says (this is not even pseudocode):
Monad "Optional": bind funcA and funcB =>
if funcA returns null, return null
otherwise, pass the result to funcB
finally, return whatever funcB returns
Then, you can use the first version of the code, but with an annotation saying that it's transformed using your monad "Optional":
function ReadValueFromConfig(key, configFile) using Optional:
var file = openFile(configFile)
var json = parseJson(file)
var value = json.get(key)
return value
In haskell the specific syntax sugar for that is called "do notation".
I fricked up the formatting, oh well
(code)text(/code) with square brackets =
text
text
You get the point
Ffs
text
Text
It's an interface with two functions
interface M<A> {
flatten(x: M<M<A>>): M<A>
return(x: A): M<A>
}
if you can flatmap it, it's a monad
Here, I'll give you the quick rundown.
Some example datatypes:
data Maybe a = Just a | Nothing
newtype State s a = State { runState :: s -> (s, a) }
Functors:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Take a function, take a value which contains another value, modify that inner value. Simple.
Example:
fmap f (Just v) = Just (f v)
fmap _ Nothing = Nothing
fmap f (State sf) = State (s ->
let (s', v) = sf s
in (s', f v))
Applicatives:
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Like functor, but the function is also contained inside a wrapped value.
The wrappers need to combine together somehow, as well as map over the internal value.
pure just creates a wrapper around a value.
Example:
pure = Just
(Just f) <*> (Just v) = Just (f v)
_ <*> _ = Nothing
pure v = State (s -> (s, v))
(State sf) <*> (State sv) = State (s ->
let (s', f) = sf s
(s'', v) = sv s'
in (s'', f v))
You can also do this:
fmap :: (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = (fmap f a) <*> b
Applicatives let you run multi-parameter functions over wrapped values, while functor only lets you run single-parameter functions.
Monads:
class (Applicative m) => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
Like functor, except the mapping function returns a new wrapped value, instead of just a new value.
In effect, this means the mapping function can modify the wrapper as well as the value, which functor cannot.
return is the same as pure.
Example:
return = pure
(Just v) >>= k = k v
Nothing >>= _ = Nothing
(State sf) >>= k = State (s ->
let (s', v) = sf s
in runState (k v) s')
Any questions?
Contexts. Context of failure. Context of state. Context of side effects. Now you want to do something with the values which are stuck in these bubble contexts. Hence, functors, applicatives and monads
functor, applicative and monad are interfaces defied by methods
functor has map
> fmap (* 2) [1, 2, 3]
[2,4,6]
applicative extends functor and has lift which maps across multiple contexts
> liftA2 (*) [1, 2] [3, 4]
[3,4,6,8]
monad extends applicative and has join which flattens nested contexts
[code> join [[1,2], [3], [5, 6, 7]]
[1,2,3,5,6,7]
any questions?
Goddamn functional programming is platinum gay. Look at all the extra work you do in order to have less performance than procedural code.
Build systems like Bazel are based on FP concepts. STFU if you don’t know what you’re talking about
Haskell is an elaborate joke to make functional programming look bad.
Check out languages such as Standard ML if you're interested: it's Haskell without the bullshit.