Cli*_*ton 9 monads haskell arrows
我试图理解箭头符号,特别是它如何与Monads一起工作.使用Monads,我可以定义以下内容:
f = (*2)
g = Just 5 >>= (return . f)
Run Code Online (Sandbox Code Playgroud)
并且g是Just 10
我如何使用箭头符号进行上述操作?
And*_*ewC 10
翻译成箭头的第一步是从思考m b自己到思考a -> m b.
有了monad,你就会写
use x = do
.....
....
doThis = do
....
...
thing = doThis >>= use
Run Code Online (Sandbox Code Playgroud)
而箭头总是有输入,所以你必须这样做
doThis' _ = do
.....
....
Run Code Online (Sandbox Code Playgroud)
然后使用(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c从Control.Monad有
thing' = doThis' >=> use
Run Code Online (Sandbox Code Playgroud)
>=>消除了不对称性>>=,我们称之为Monad的Kleisli箭头.
()输入或"如果我的第一件事情真的不是一个函数关系吗?"没关系,如果你的monad没有产生任何东西(比如putStrLn没有),那就是共同的问题,于是你就得到了它return ().
如果你的东西不需要任何数据,只需将它()作为一个参数的函数.
doThis()=做....
这样,everthing有签名a -> m b,你可以链接它们>=>.
箭头有签名
Arrow a => a b c
Run Code Online (Sandbox Code Playgroud)
这可能不如中缀明确
Arrow (~>) => b ~> c
Run Code Online (Sandbox Code Playgroud)
但是你仍然应该把它看作是一个类似的东西b -> m c.
主要的区别在于,b -> m c你有b一个函数的参数,你可以用它做你喜欢的事情,比如if b == "war" then launchMissiles else return ()用箭头你不能(除非它是一个ArrowApply - 看看这个问题为什么ArrowApply给你Monad功能) - 一般来说,箭头只是做它做的事情而不是基于数据切换操作,有点像Applicative那样做.
问题b -> m c在于你不能在实例声明中部分地应用它来-> m从中间获取位,因此,假设b -> m c被称为Kleisli箭头,Control.Monad定义(>>>)为在完成所有包装和解包之后,你得到f >>> g= \x -> f x >>= g- 但是这个相当于(>>>) = (>=>).(事实上,(.)是为类别定义的,而不是前锋组合>>>,但我确实说过等同!)
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
instance Monad m => Category (Kleisli m) where
id = Kleisli return
(Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f) -- composition of Kleisli arrows
instance Monad m => Arrow (Kleisli m) where
arr f = Kleisli (return . f)
first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))
Run Code Online (Sandbox Code Playgroud)
(尽量忽略所有的Kleisli和runKleisli-他们只是在包装和展开一元价值观-当你定义自己的方向,他们没有必要)
如果我们展开那意味着什么Maybe,我们得到相当于作曲
f :: a -> Maybe b
g :: b -> Maybe c
f >>> g :: a -> Maybe c
f >>> g = \a -> case f a of -- not compilable code!
Nothing -> Nothing
Just b -> g b
Run Code Online (Sandbox Code Playgroud)
而应用(纯)函数的箭头方式是 arr :: Arrow (~>) => (b -> c) -> b ~> c
我会明确(~->)表示Kleisli Maybe你可以看到它的实际效果:
{-# LANGUAGE TypeOperators #-}
import Control.Arrow
type (~->) = Kleisli Maybe
g :: Integer ~-> Integer
g = Kleisli Just >>> arr (*2)
Run Code Online (Sandbox Code Playgroud)
给
ghci> runKleisli g 10
Just 20
Run Code Online (Sandbox Code Playgroud)
do符号一样,但输入和输出.(GHC)GHC实现了相当于do表示法,proc表示法,它可以让你这样做
output <- arrow -< input
Run Code Online (Sandbox Code Playgroud)
你已经习惯了,output <- monad但现在有了arrow -< input符号.就像Monads一样,你没有<-在最后一行做,你也没有在proc表示法中这样做.
让我们使用Maybe版本的tail并从safe中读取来说明符号(和广告safe).
{-# LANGUAGE Arrows #-}
import Control.Arrow
import Safe
this = proc inputList -> do
digits <- Kleisli tailMay -< inputList
number <- Kleisli readMay -<< digits
arr (*10) -<< number
Run Code Online (Sandbox Code Playgroud)
注意我已经使用了-<<变量-<,它允许你使用输出作为输入,将左边的东西放到右边的<-范围内-<.
显然this相当于Kleisli tailMay >>> Kleisli readMay >>> arr (*10),但它只是(!)给你的想法.
ghci> runKleisli this "H1234" -- works
Just 1234
ghci> runKleisli this "HH1234" -- readMay fails
Nothing
ghci> runKleisli this "" -- tailMay fails
Nothing
ghci> runKleisli this "10" -- works
Just 0
Run Code Online (Sandbox Code Playgroud)
()就像我说的那样,()如果我们没有输入,我们会使用,就像我们在Monad中一样,如果我们不需要输出任何内容,则返回它.
你也会()在proc符号例子中看到:
thing = proc x -> do
this <- thing1 -< ()
() <- thing2 -< x
returnA -< this
Run Code Online (Sandbox Code Playgroud)
首先,我们需要一个与Maybemonad 具有相同语义的箭头.我们可以从头开始定义它,但最简单的方法是将Maybemonad 包装成Kleisli:
type MaybeArrow = Kleisli Maybe
Run Code Online (Sandbox Code Playgroud)
然后我们还需要一种方法来运行这个monad来提取结果:
runMaybeArrow :: MaybeArrow () a -> Maybe a
runMaybeArrow = flip runKleisli ()
Run Code Online (Sandbox Code Playgroud)
另外,如何从给定值(只是忽略其输入)创建一个常量箭头是很方便的:
val :: (Arrow a) => c -> a b c
val = arr . const
Run Code Online (Sandbox Code Playgroud)
最后,我们得到:
g' = runMaybeArrow (val 5 >>> arr f)
Run Code Online (Sandbox Code Playgroud)