将Monad表示法转换为Arrow表示法 [英] Converting Monad notation to Arrow notation

查看:111
本文介绍了将Monad表示法转换为Arrow表示法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解箭头符号,特别是它在Monads中的工作方式.使用Monad,我可以定义以下内容:

I'm trying to understand arrow notation, in particularly how it works with Monads. With Monads I can define the following:

f = (*2)
g = Just 5 >>= (return . f)

gJust 10

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

How do I do the above but using arrow notation?

推荐答案

将您的Monad思想转变为Arrow思想

翻译成Arrow的第一步是从对m b的独立思考转变为对a -> m b的思考.

Changing your Monad thinking to Arrow thinking

The first step to translating into Arrow is to move from thinking about m b on its own to thinking about a -> m b.

一个单子,你会写

use x = do
   .....
   ....
doThis = do
   ....
   ...

thing = doThis >>= use

而箭头总是有输入,因此您必须这样做

whereas an arrow always has an input, so you'd have to do

doThis' _ = do
   .....
   ....

,然后使用Control.Monad中的(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c

thing' = doThis' >=> use

>=>消除了>>=的不对称性,这就是我们所说的Monad的Kleisli箭头.

>=> removes the asymmetry of >>=, and is what we would call the Kleisli arrow of the Monad.

没关系,这是一个问题,如果您的monad不产生任何东西(例如putStrLn不产生任何东西),那么您只需将其发送到return ().

That's OK, it's just the co-problem to if your monad doesn't produce anything (like putStrLn doesn't), whereupon you just get it to return ().

如果您的东西不需要任何数据,只需使其成为以()作为参数的函数即可.

If your thing doesn't need any data, just make it a function that takes () as an argument.

doThis()=做 .... ....

doThis () = do .... ....

那样,所有东西都具有签名a -> m b,您可以将它们与>=>链接起来.

that way everthing has the signature a -> m b and you can chain them with >=>.

箭头具有签名

Arrow a => a b c

这可能不如中缀

Arrow (~>) => b ~> c

,但您仍应将其视为与b -> m c类似.

but you should still be thinking of it as analagous to b -> m c.

主要区别在于,使用b -> m c,您可以将b作为函数的参数,并且可以使用它来完成所需的操作,例如if b == "war" then launchMissiles else return (),但是使用箭头则不能(除非是ArrowApply -请参阅此问题,以了解ArrowApply为什么为您提供Monad功能)-一般,只是一个箭头会根据数据执行操作而不会切换操作,就像Applicative一样.

The main difference is that with b -> m c you have your b as an argument to a function and can do what you like with it, like if b == "war" then launchMissiles else return () but with an arrow you can't (unless it's an ArrowApply - see this question for why ArrowApply gives you Monad capabilities) - in general, an arrow just does what it does and doesn't get to switch operation based on the data, a bit like an Applicative does.

b -> m c的问题在于,您不能在实例声明中部分地应用它来从中间获取-> m位,因此鉴于b -> m c被称为Kleisli箭头,Control.Monad定义了(>>>),以便在所有包装和展开之后,您得到f >>> g = \x -> f x >>= g-但这等同于(>>>) = (>=>). (实际上,(.)是为类别定义的,而不是前向组合物>>>的定义,但我确实说过等效!)

The problem with b -> m c is that there you can't partially apply it in an instance declaration to get the -> m bit from the middle, so given that b -> m c is called a Kleisli arrow, Control.Monad defines (>>>) so that after all the wrapping and unwrapping, you get f >>> g = \x -> f x >>= g - but this is equivalent to (>>>) = (>=>). (In fact, (.) is defined for Categories, rather than the forwards composition >>>, but I did say equivalent!)

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

您的例子,最后

(尝试忽略所有KleislirunKleisli-它们只是包裹和解开单调值-当您定义自己的箭头时,它们不是必需的.)

Your example, at last

(Try to ignore all the Kleisli and runKleisli - they're just wrapping and unwrapping monadic values - when you define your own arrow, they're not necessary.)

如果解开Maybe的含义,我们将获得与

If we unwrap what that means for the Maybe, we get the equivalent of composing

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

arr :: Arrow (~>) => (b -> c) -> b ~> c

我将修复(~->)的意思是Kleisli Maybe,以便您可以实际使用它:

I'll fix (~->) to mean Kleisli Maybe so you can see it in action:

{-# LANGUAGE TypeOperators #-}
import Control.Arrow
type (~->) = Kleisli Maybe

g :: Integer ~-> Integer
g = Kleisli Just >>> arr (*2)

给予

ghci> runKleisli g 10
Just 20

类似于do表示法,但具有输入和输出. (GHC)

GHC实现了与do表示法等效的proc表示法,您可以这样做

Like do notation, but with input as well as output. (GHC)

GHC implements the equivalent of do notation, proc notation, which lets you do

output <- arrow -< input

您已经习惯了output <- monad,但是现在有了arrow -< input表示法.就像Monads一样,您在最后一行也不会执行<-,您也不会在proc表示法中也这样做.

You're used to output <- monad but now there's the arrow -< input notation. Just as with Monads, you don't do <- on the last line, you don't do that in proc notation either.

让我们使用tail的Maybe版本,并从 safe 中进行阅读以说明该符号(以及广告safe).

Let's use the Maybe versions of tail and read from safe to illustrate the notation (and advertise safe).

{-# LANGUAGE Arrows #-}
import Control.Arrow
import Safe

this = proc inputList -> do
    digits <- Kleisli tailMay -< inputList
    number <- Kleisli readMay -<< digits
    arr (*10) -<< number

注意,我使用了-<-<<变体,通过将<-左侧的内容放到-<右侧的范围内,可以将输出用作输入.

Notice I've used the -<< variant of -<, which lets you use output as input by bringing things on the left of <- into scope at the right of -<.

很显然,this等同于Kleisli tailMay >>> Kleisli readMay >>> arr (*10),但这只是(!)可以帮助您理解.

Clearly this is equivalent to Kleisli tailMay >>> Kleisli readMay >>> arr (*10), but it's just (!) to give you the idea.

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

所有()

就像我说的,如果没有输入,我们将使用(),就像我们在Monad中所做的那样,如果不需要输出任何内容,则将其返回.

All that ()

Like I said, we use () if we don't have input, and as we do in Monad, return it if we don't need to output anything.

您还将在proc表示法示例中看到():

You'll see () in proc notation examples too:

thing = proc x -> do
     this <- thing1 -< ()
     () <- thing2 -< x
     returnA -< this

这篇关于将Monad表示法转换为Arrow表示法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆