简体   繁体   English

减少Haskell中的Monads

[英]Reducing over Monads in Haskell

Suppose I have a type defined as: 假设我的类型定义为:

data Node = forall a b. Node (SimpleWire a b)

The SimpleWire is a monad, where a represents the inputs, and b represents the outputs. SimpleWire是一个monad,其中a代表输入, b代表输出。 I can do function commposition over that monad. 我可以对那个monad做功能组合。 So suppose I have wireA of type SimpleWire AB , and wireB of type SimpleWire BC , doing wireA . wireB 因此,假设我有wireASimpleWire ABwireBSimpleWire BC ,做wireA . wireB wireA . wireB would give me the type SimpleWire AC . wireA . wireB会给我类型SimpleWire AC

Now I want to fold over a list of that monad (so of type [Node] for this case). 现在我想折叠该monad的列表(对于这种情况,类型为[Node] )。 Something like: 就像是:

buildGraph :: [Node] -> (SimpleWire a b)
buildGraph (Node h):t = h . (buildGraph t)

How do I make this code work in Haskell's type system? 如何在Haskell的类型系统中使用此代码?

We can not compose [Node] with the proposed types. 我们不能用建议的类型组成[Node] This is because otherwise we would get 这是因为否则我们会得到

sw1 :: SimpleWire A B
sw2 :: SimpleWire C D
buildGraph :: [Node] -> (SimpleWire a b)
buildGraph [ sw1, sw2 ] :: SimpleWire E F

Which is way too strong. 这太强大了。 We were able to compose arbitrary, incompatible types (wrong), and then to result with a random write at the very end (wrong). 我们能够组成任意的,不兼容的类型(错误的),然后在最后一个随机写入(错误)。

The problem is that we lost all the type information in the [Node] type. 问题是我们丢失了[Node]类型中的所有类型信息。 We need to remember some, namely: 我们需要记住一些,即:

  1. The first and last wire types are known (the intermediate ones are not) 第一种和最后一种导线类型是已知的(中间导线类型不是)
  2. In the list, every adjacent node is composable 在列表中,每个相邻节点都是可组合的

So, we get a custom GADT list type 因此,我们获得了自定义GADT列表类型

data NodeList a b where
   Nil  :: NodeList a a
   Cons :: Node a b -> NodeList b c -> NodeList a c

And then 接着

buildGraph :: NodeList a b -> SimpleWire a b
buildGraph Nil = id  
buildGraph (Cons (Node h) t) = h . buildGraph t

I'm going to assume the following story: 我将假设以下故事:

You probably used the type 你可能用过这种类型

data Node = forall a b. Node (SimpleWire a b)

instead of just SimpleWire ab because you wanted a list of SimpleWire 's where a and b are different. 而不仅仅是SimpleWire ab因为你想要一个SimpleWire的列表,其中ab是不同的。 In particular, what you were really hoping for as the argument to buildGraph was something like (in pseudo-Haskell) 特别是,你真正希望作为buildGraph的参数是什么(在伪Haskell中)

buildGraph :: [SimpleWire a b, SimpleWire b c, ..., SimpleWire x y] -> SimpleWire a y

You couldn't express that first list with Haskell's standard homogeneous [] though and tried to use universally-quantified types to get you out of that pickle. 你不能用Haskell的标准齐次[]来表达第一个列表,并尝试使用普遍量化的类型来让你摆脱那个泡菜。

If what I've said is true, you're probably looking for type-threaded lists or "thrists" . 如果我说的是真的,你可能正在寻找类型线程列表或“thrists” In particular, you can do away with Node altogether. 特别是,您可以完全取消Node A Thrist (->) ab is a list of functions a -> a1 , a1 -> a2 , ..., an -> b . Thrist (->) ab是函数列表a -> a1a1 -> a2 ,..., an -> b More generally a Thrist fab is a list of f s fa a1 , f a1 a2 , ..., f an b . 更一般地说, Thrist fabf s fa a1f a1 a2 ,..., f an b

{-# LANGUAGE GADTs #-}
import qualified Data.Thrist as DT

-- Note that I'll be using (>>>) as a flipped form of (.), i.e. 
-- (>>>) = flip (.)
-- (>>>) is in fact an Arrow operation which is significantly more general
-- than function composition. Indeed your `SimpleWire` type is almost
-- definitely an arrow.
import Control.Arrow ((>>>))

-- A simple take on SimpleWire
type SimpleWire = (->)

-- Ugh a partial function that blows up if the thrist is empty
unsafeBuildGraph :: DT.Thrist SimpleWire a b -> SimpleWire a b
unsafeBuildGraph = DT.foldl1Thrist (>>>)

-- Making it total
buildGraph :: DT.Thrist SimpleWire a b -> Maybe (SimpleWire a b)
buildGraph DT.Nil = Nothing
buildGraph (wire `DT.Cons` rest) = Just $ DT.foldlThrist (>>>) wire rest

-- For syntactic sugar
(*::*) = DT.Cons
infixr 6 *::*

trivialExample :: DT.Thrist SimpleWire a a
trivialExample = id *::* id *::* DT.Nil

lessTrivialExample :: (Num a, Show a) => DT.Thrist SimpleWire a String
lessTrivialExample = (+ 1) *::* (* 2) *::* show *::* DT.Nil

-- result0 is "12"
result0 = (unsafeBuildGraph lessTrivialExample) 5

-- result1 is Just "12"
result1 = fmap ($ 5) (buildGraph lessTrivialExample)

A side note: 附注:

Although SimpleWire may very well be a monad, that's probably not going to directly help you. 虽然SimpleWire很可能是一个monad,但这可能不会直接帮助你。 In particular while functions are monads, what you seem to care about is generalizing over the notion of function composition, which is what arrows are for (and which bear only an indirect relationship to monads). 特别是当函数是monad时,你似乎关心的是概括函数组合的概念,这是箭头的含义(并且它只与monad有间接关系)。 There are hints of this in the fact that I used >>> and that Thrist has an Arrow instance. 事实上,我使用>>>并且Thrist有一个Arrow实例。 As I mention in the comments to the code, SimpleWire is probably an Arrow . 正如我在代码的评论中提到的, SimpleWire可能是一个Arrow

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

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