在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):anton-k/monads-for-drummers开源软件地址(OpenSource Url):https://github.com/anton-k/monads-for-drummers开源编程语言(OpenSource Language):开源软件介绍(OpenSource Introduction):Monads for DrummersI'd like to explain the Haskell's Monad class. There are so many monad tutorials. Do we need yet another one? But no one tried to explain it so that even a drummer can understand it! It's just a joke, there is another reason. The need for writing this tutorial comes from the desire to translate a chapter from my book on Haskell that is written in Russian. Many people claimed that they could finally get into Monads after reading my explanation. So I'd like to make it available for English speaking community. The need for monadDo we really need monads after all? The Python, Java or Scala folks can do cool stuff without it. Can we do the things we want with Haskell as we accustomed to with imperative languages and leave the Monads aside as some brainy concept. Brainy concept for the beauty of functional programming. Well, ok, ok. But we are real coders. We can code without Monads! It happens that the answer is NO. We can not do anything real in Haskell without grasping the Monad concept. Since the Monads are responsible for input/output in the Haskell. But wait a minute so many languages can live without Monads, why do we need them in Haskell? Well, the short answer is because Haskell is different. It's not like anything else. Let me show the long answer. Order of execution (equational reasoning)Let's start with a small program. Let's prompt the user for a string and then another one. We are going to return a string that contains both strings. Let me write it in Python: def getBoth():
x = input()
y = input()
return (x + y) Don't worry if you don't know the Python. It's not that hard to understand.
With Let's write another program. It reads the input only once and returns it twice. It looks like this in Python: def getOne():
x = input()
return (x + x) Pretty clean code. Can we translate it to Haskell? Let's try: getBoth () = res
where
x = input ()
y = input ()
res = x ++ y Let's try to translate the second example. We want the user to type only once: getOne () = res
where
x = input ()
res = x ++ x Unfortunately (or maybe for a good reason) it's not going to work.
we are about to see what makes Haskell so different from Python or
from many other languages. In Python we have explicit order of execution.
Do this line then do the next one if you are done make the third one and so on.
So when the Python executes x = input() // 1:
y = input() // 2:
return (x + y) // 3: return the result But the Haskell executes statements by functional dependencies!
Let's execute the
Well we need to query user twice for input, concatenate strings
and we are done! The funny things start to show off when we try
to apply the same procedure to the function So the we need to calculate the
The problem is that from the Haskell's point of view there is no
difference between Recall that you can write functions in any order. The Equational reasoning is an assumption that we can always substitute
expressions with their definitions. But we can clearly see how this
strategy prevents us from writing useful programs.
If we can not write so simple programs as Wait a moment! Monads are going to save the Haskell out of trouble! Or introduce many more troubles for the novices.. trying to grasp all this stuff. Here is the Key ideaMonads can introduce the order of execution!With monads we can simulate this line by line behavior.
But let's dive deeper into the notion of the order.
Let's stretch our brains a little bit.
Let's recall the concept of class Monoid a where
mempty :: a
(<>) :: a -> a -> a We have two methods. There are rules: mempty <> a === a
a <> mempty === a
a <> (b <> c) === (a <> b) <> c So there is a neutral element Let's look at one example. The list of things is a monoid: instance Monoid [a] where
mempty = []
a <> b = a ++ b So the id x = x
(g . f) x = g (f x)
instance Monoid (a -> a) where
mempty = id
f <> g = g . f The Let's return to the Python. Let's imagine that with some monoid instance we can sequence the order of execution: (x = input ())
<> (y = input ())
<> (return (x + y)) The (x = input ())
<> (y = input ()) becomes input ()
<> (\x -> y = input ()) And we can also rewrite the input ()
<> (\x ->
input ()
<> \y -> return (x ++ y)
) In fact that is exactly what Monad class is doing for user input/output. Let's look at the class Monad: class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b Don't try to understand it! I think a lot of confusion comes with this definition. It's not clear how it relates to ordering. But we can study a structure that is equivalent to the Monad. It's called a Kleisli category. class Kleisli m where
idK :: a -> m a
(*>) :: (a -> m b) -> (b -> m c) -> (a -> m c) It defines two methods: the identity function idK :: a -> a
(*>) :: (a -> b) -> (b -> c) -> (a -> c) We can represent this with picture: There are rules for idK *> a === a
a *> idK === a
(a *> b) *> b === a *> (b *> b) The The module Main where
import Prelude hiding ((*>))
class Kleisli m where
idK :: a -> m a
(*>) :: (a -> m b) -> (b -> m c) -> (a -> m c) ExamplesThe partially defined functions (Maybe)There are functions that are defined not for all inputs. They can return the value or fail. There is a function head (x:xs) = x
head [] = error "crash" We can see that it's undefined for the empty list.
There is a special type data Maybe a = Nothing | Just a We can define a safe version of the headSafe :: [a] -> Maybe a
headSafe xs = case xs of
(x:_) -> Just x
[] -> Nothing Also we can define a function that extracts a tail from the list safely: tailSafe :: [a] -> Maybe [a]
tailSafe xs = case xs of
(_:as) -> Just as
[] -> Nothing What if we want to define the function that safely extracts the second element from the list? It would be nice to define it like this: secondSafe = headSafe . tailSafe So the second element is just the But if there is an instance of Monad or Kleisli for Maybe, we can define it like this: secondSafe = tailSafe *> headSafe The third element is not that difficult to define: thirdSafe = tailSafe *> tailSafe *> headSafe To appreciate the usefulness of this approach I'm going to define
the secondSafe :: [a] -> Maybe a
secondSafe xs = case xs of
(_:x:_) -> Just x
_ -> Nothing Let's look at the definition of composition for functions with If the first function produces the value we are going to apply the second function.
If the first function or second one fails we are going to return instance Kleisli Maybe where
idK a = Just a
f *> g = \x -> case f x of
Just a -> g a
Nothing -> Nothing Functions that can return many valuesSome functions can return multiple values. They can return list of results. As an example consider the L-systems. They model the growth of a being. The being is organized with a row of cells. Each cell can divide or split in many cells. We can encode cells with letters and the splitting of cells with rules. a -> ab
b -> a Let's start with a
ab
aba
abaab
abaababa We can model the rules with Haskell: next :: Char -> String
next 'a' = "ab"
next 'b' = "a" Then with Kleisli instance it's very easy to define the growth function. Let's see how the composition is implemented: We apply the second function to all outputs of the first function and we concatenate all results into the single list. instance Kleisli [] where
idK = \a -> [a]
f *> g = \x -> concat (map g (f x)) Let's define the growth function: generate :: Int -> (a -> [a]) -> (a -> [a])
generate 0 f = idK
generate n f = f *> generate (n - 1) f We can try it with > let gen n = generate n next 'a'
> gen 0
"a"
> gen 1
"ab"
> gen 2
"aba"
> gen 3
"abaab"
> gen 4
"abaababa" Functions with stateAnother useful function is a function with a state. It calculates its result based on some state value. It expects the initial state and it returns
the pair of value and updated state. In Haskell it's defined
in the module data State s a = State { runState :: s -> (a, s) } Can you guess the Kleisli definition by looking at the picture of composition? The Monad classSo we can see that Kleisli is all about sequencing things.
Let's take a closer look at class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b We can see that a -> (a -> b) -> b What if we reverse the arguments: (a -> b) -> a -> b It's an application of the function to the value!
So the monadic bind method is just an application
of the monadic value (wrapped in some structure instance Kleisli m => Monad m where
return = idK
a >>= f = ((\_ -> a) *> f) () We first create a function out of the value and we apply the function to an empty tuple to express application with composition. Also we can define instance Monad m => Kleisli m where
idK = return
f *> g = \x -> x f >>= g Let's turn back to the example with user input. How can we rewrite the three simple lines with Haskell: x = input ()
y = input ()
return (x ++ y) Can you recall that we transformed it with monoid methods: (x = input ())
<> (y = input ())
<> (return (x + y)) and then later to input ()
<> (\x ->
input ()
<> \y -> return (x ++ y)
) It turns out that the monoid sequencing input ()
>>= (\x ->
input ()
>>= \y -> return (x ++ y)
) And we can rewrite the second example that way: x = input
return x ++ x With Haskell it becomes:
We can see that with Monads the two examples are different! That is the magic of sequencing with Monads. But you can say ooh my God! Do I need to write those simple Python programs with monstrous expressions like this: input ()
>>= (\x ->
input ()
>>= \y -> return (x ++ y)
) In fact you don't need to do it. The clever Haskell language designers
created a special syntactic sugar to make Haskell look like an imperative language.
It's called Ask twice: getBoth () = do
x <- getLine
y <- getLine
return (x ++ y) Ask once: getOne () = do
x <- getLine
return (x ++ x) I've substituted the Python's name Let's see how do
x <- expr1 => expr1 >>= \x ->
y <- expr2 expr2 >>= \y ->
f x y f x y So the left hand side of the arrow ConclusionsSo we have a nice and clean way to do imperative programming with Haskell!
The imperative languages like Java or Python also have monads.
It's better to say that they have THE MONAD. It's built in
and it's hidden from the user. It's so much in the bones
of the language that we tend not to care about it. It just works!
But the Haskell is different! With Haskell we can see that there are
plenty of different monads and we can redefine the The original need for Monads was induced by desire to implement input/output in the purely functional setting. But the smart eyes of Haskell developers could see deeper. We can define many different default behaviors with the same interface. The interface of sequencing! BonusReaderThere are other monads. The reader monad is for functions that can access some "global" state or environment. They can only read the value of the state. It's defined like this in the Haskell code: data Reader r a = Reader (r -> a) Can you define WriterThe writer monad is another special case of a state Monad. The writer can process the values and accumulate some state. The accumulation of the state is expressed with Monoid methods: data Writer w a = Writer (a, w)
instance Monoid w => Monad (Writer w) where
return a = Writer (a, mempty)
a >>= f = ... Can you complete the FunctorSometimes the monadic bind m a -> (a -> m b) -> m b We just want to apply a pure function to a monadic value: m a -> (a -> b) -> m b That's what class Functor m where
fmap :: (a -> b) -> m a -> m b Notice that the order of the arguments is reversed. We rewrite our ask once example with a single line: > fmap (\x -> x ++ x) getLine ApplicativeOk, with
The f <$> ma <*> mb <*> mc or liftA3 f ma mb mc There are also 全部评论
专题导读
上一篇:GaloisInc/xml: Haskell XML library发布时间:2022-06-24下一篇:MichaelXavier/cron: Cron data structure and parser for Haskell发布时间:2022-06-24热门推荐
热门话题
阅读排行榜
|
请发表评论