将Monad表示法转换为箭头表示法

Cli*_*ton 9 monads haskell arrows

我试图理解箭头符号,特别是它如何与Monads一起工作.使用Monads,我可以定义以下内容:

f = (*2)
g = Just 5 >>= (return . f)
Run Code Online (Sandbox Code Playgroud)

并且gJust 10

我如何使用箭头符号进行上述操作?

And*_*ewC 10

改变你的Monad思想到Arrow思考

翻译成箭头的第一步是从思考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 cControl.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那样做.

将Monads转换为箭头

问题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)

你的例子,最后

(尽量忽略所有的KleislirunKleisli-他们只是在包装和展开一元价值观-当你定义自己的方向,他们没有必要)

如果我们展开那意味着什么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)


Pet*_*lák 8

首先,我们需要一个与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)