简体   繁体   English

计算构造(Monads,Arrows等)

[英]Computation Constructs (Monads, Arrows, etc.)

I have become rather interested in how computation is modeled in Haskell. 我对如何在Haskell中建模计算感兴趣。 Several resources have described monads as "composable computation" and arrows as "abstract views of computation". 一些资源将monad描述为“可组合计算”,将箭头描述为“计算的抽象视图”。 I've never seen monoids, functors or applicative functors described in this way. 我从未见过用这种方式描述的幺半群,仿函数或应用函子。 It seems that they lack the necessary structure. 他们似乎缺乏必要的结构。

I find that idea interesting and wonder if there are any other constructs that do something similar. 我发现这个想法很有趣,并想知道是否有其他类似的结构。 If so, what are some resources that I can use to acquaint myself with them? 如果是这样,我可以使用哪些资源来熟悉它们? Are there any packages on Hackage that might come in handy? Hackage上是否有任何可能派上用场的软件包?

Note: This question is similar to Monads vs. Arrows and https://stackoverflow.com/questions/2395715/resources-for-learning-monads-functors-monoids-arrows-etc , but I am looking for constructs beyond funtors, applicative functors, monads, and arrows. 注意:这个问题类似于Monads vs. Arrowshttps://stackoverflow.com/questions/2395715/resources-for-learning-monads-functors-monoids-arrows-etc ,但我正在寻找超越funtors的构造,applicative仿函数,单子和箭头。

Edit: I concede that applicative functors should be considered "computational constructs", but I'm really looking for something I haven't come across yet. 编辑:我承认应用仿函数应该被视为“计算结构”,但我真的在寻找一些我还没有遇到过的东西。 This includes applicative functors, monads and arrows. 这包括应用函子,monad和箭头。

Arrows are generalized by Categories, and so by the Category typeclass. Arrows按类别进行推广,因此Category类型类别也是如此。

 class Category f where
     (.) :: f a b -> f b c -> f a c
     id :: f a a

The Arrow typeclass definition has Category as a superclass. Arrow类型类定义将Category作为超类。 Categories (in the haskell sense) generalize functions (you can compose them but not apply them) and so are definitely a "model of computation". 类别(在haskell意义上)概括函数(你可以组合它们但不应用它们)因此绝对是“计算模型”。 Arrow provides a Category with additional structure for working with tuples. ArrowCategory提供了使用元组的附加结构。 So, while Category mirrors something about Haskell's function space, Arrow extends that to something about product types. 因此,虽然Category反映了Haskell函数空间的某些内容,但Arrow将其扩展到了有关产品类型的内容。

Every Monad gives rise to something called a "Kleisli Category" and this construction gives you instances of ArrowApply . 每个Monad都会产生一种称为“Kleisli类别”的东西,这种结构为你提供了ArrowApply实例。 You can build a Monad out of any ArrowApply such that going full circle doesn't change your behavior, so in some deep sense Monad and ArrowApply are the same thing. 你可以用任何一个ArrowApply构建一个Monad ,这样整圆就不会改变你的行为,所以从某种意义上讲, MonadArrowApply是一回事。

 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)

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

Actually every Arrow gives rise to an Applicative (universally quantified to get the kinds right) in addition to the Category superclass, and I believe the combination of the appropriate Category and Applicative is enough to reconstruct your Arrow . 实际上,除了Category超类之外,每个Arrow都会产生一个Applicative (普遍量化以获得正确的类型),我相信适当的CategoryApplicative的组合足以重建你的Arrow

So, these structures are deeply connected. 因此,这些结构是紧密相连的。

Warning: wishy-washy commentary ahead . 警告:未来的评论 One central difference between the Functor / Applicative / Monad way of thinking and the Category / Arrow way of thinking is that while Functor and its ilk are generalizations at the level of object (types in Haskell), Category / Arrow are generelazation of the notion of morphism (functions in Haskell). Functor / Applicative / Monad思维方式和Category / Arrow思维方式之间的一个中心区别在于,虽然Functor及其类似物是对象层面的概括(Haskell中的类型),但Category / Arrow是对概念的全新化。 态射 (Haskell中的函数)。 My belief is that thinking at the level of generalized morphism involves a higher level of abstraction than thinking at the level of generalized objects . 我的观点是,在广义态射的层面上思考涉及比在广义对象层次上思考更高层次的抽象。 Sometimes that is a good thing, other times it is not. 有时这是好事,有时则不是。 On the other-hand, despite the fact that Arrows have a categorical basis, and no one in math thinks Applicative is interesting, it is my understanding that Applicative is generally better understood than Arrow . 另一方面,尽管Arrows有一个明确的基础,并且数学中没有人认为Applicative是有趣的,但我的理解是Applicative通常比Arrow更好理解。

Basically you can think of "Category < Arrow < ArrowApply" and "Functor < Applicative < Monad" such that "Category ~ Functor", "Arrow ~ Applicative" and "ArrowApply ~ Monad". 基本上你可以想到“Category <Arrow <ArrowApply”和“Functor <Applicative <Monad”这样的“Category~Functor”,“Arrow~Applied”和“ArrowApply~Monad”。

More Concrete Below: As for other structures to model computation: one can often reverse the direction of the "arrows" (just meaning morphisms here) in categorical constructions to get the "dual" or "co-construction". 下面更具体:对于模型计算的其他结构:人们通常可以在分类结构中反转“箭头”的方向(这里只是意味着态射),以获得“双重”或“共同构建”。 So, if a monad is defined as 因此,如果将monad定义为

class Functor m => Monad m where
   return :: a -> m a
   join :: m (m a) -> m a

(okay, I know that isn't how Haskell defines things, but ma >>= f = join $ fmap f ma and join x = x >>= id so it just as well could be) then the comonad is (好吧,我知道这不是Haskell定义的东西,但是ma >>= f = join $ fmap f majoin x = x >>= id所以它也可以)然后comonad是

class Functor m => Comonad m where
   extract :: m a -> a -- this is co-return
   duplicate :: m a -> m (m a) -- this is co-join

This thing turns out to be pretty common also. 这件事也很常见。 It turns out that Comonad is the basic underlying structure of cellular automata . 事实证明, Comonad细胞自动机的基本结构。 For completness, I should point out that Edward Kmett's Control.Comonad puts duplicate in a class between functor and Comonad for "Extendable Functors" because you can also define 为了完整性,我应该指出Edward Kmett的Control.Comonad在functor和Comonad之间的类中为“可扩展的函数” Comonadduplicate ,因为你也可以定义

   extend :: (m a -> b) -> m a -> m b -- Looks familiar? this is just the dual of >>=
   extend f = fmap f . duplicate
   --this is enough
   duplicate = extend id

It turns out that all Monad s are also "Extendable" 事实证明,所有Monad都是“可伸展的”

   monadDuplicate :: Monad m => m a -> m (m a)
   monadDuplicate = return

while all Comonads are "Joinable" 而所有Comonads都是“可加入的”

   comonadJoin :: Comonad m => m (m a) -> m a
   comonadJoin = extract

so these structures are very close together. 所以这些结构非常接近。

All Monads are Arrows (Monad is isomorphic to ArrowApply). 所有Monad都是箭头(Monad与ArrowApply同构)。 In a different way, all Monads are instances of Applicative , where <*> is Control.Monad.ap and *> is >> . 以不同的方式,所有Monads都是Applicative的实例,其中<*>Control.Monad.ap并且*>>> Applicative is weaker because it does not guarantee the >>= operation. 应用较弱,因为它不保证>>=操作。 Thus Applicative captures computations that do not examine previous results and branch on values. 因此,Applicative捕获不检查先前结果和分支值的计算。 In retrospect much monadic code is actually applicative, and with a clean rewrite this would happen. 回想起来,很多monadic代码实际上是适用的,并且通过干净的重写会发生这种情况。

Extending monads, with recent Constraint kinds in GHC 7.4.1 there can now be nicer designs for restricted monads . 使用GHC 7.4.1中最近的约束类型扩展monad,现在可以有更好的受限monad设计。 And there are also people looking at parameterized monads , and of course I include a link to something by Oleg . 还有人在看参数化的monad ,当然我还包括Oleg的链接。

In libraries these structures give rise to different type of computations. 在库中,这些结构产生不同类型的计算。

For example Applicatives can be used to implement static effects. 例如,Applicatives可用于实现静态效果。 With that I mean effects, which are defined at forehand. 我的意思是效果,正面定义。 For example when implementing a state machine, rejecting or accepting an input state. 例如,在实现状态机时,拒绝或接受输入状态。 They can't be used to manipulate their internal structure in terms of their input. 它们不能用于根据输入操纵其内部结构。

The type says it all: 类型说明了一切:

 <*> :: f (a -> b) -> f a -> f b

It is easy to reason, the structure of f cannot be depend om the input of a. 很容易理解,f的结构不能取决于a的输入。 Because a cannot reach f on the type level. 因为a在类型级别上无法达到f。

Monads can be used for dynamic effects. Monads可用于动态效果。 This also can be reasoned from the type signature: 这也可以从类型签名中推断出来:

 >>= :: m a -> (a -> m b) -> m b

How can you see this? 你怎么看这个? Because a is on the same "level" as m. 因为a与m处于同一“级别”。 Mathematically it is a two stage process. 从数学上讲,这是一个两阶段的过程。 Bind is a composition of two function: fmap and join. Bind是两个函数的组合:fmap和join。 First we use fmap together with the monadic action to create a new structure embedded in the old one: 首先,我们将fmap与monadic动作一起使用来创建嵌入旧结构的新结构:

fmap :: (a -> b) -> m a -> m b
f :: (a -> m b)
m :: m a
fmap f :: m a -> m (m b)
fmap f m :: m (m b)

Fmap can create a new structure, based on the input value. Fmap可以根据输入值创建新结构。 Then we collapse the structure with join, thus we are able to manipulate the structure from within the monadic computation in a way that depends on the input: 然后我们用连接来折叠结构,因此我们能够以依赖于输入的方式在monadic计算中操纵结构:

join :: m (m a) -> m a
join (fmap f m) :: m b

Many monads are easier to implement with join: 使用join可以更轻松地实现许多monad:

(>>=) = join . fmap 

This is possible with monads: monads可以实现这一点:

addCounter :: Int -> m Int () 

But not with applicatives, but applicatives (and any monad) can do things like: 但不是应用程序,但应用程序(和任何monad)可以做的事情,如:

addOne :: m Int ()

Arrows give more control over the input and the output types, but for me they really feel similar to applicatives. 箭头可以更好地控制输入和输出类型,但对我来说,它们确实与应用程序类似。 Maybe I am wrong about that. 也许我错了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM