[英]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
因此,假设我有
wireA
型SimpleWire AB
和wireB
型SimpleWire 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: 我们需要记住一些,即:
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
的列表,其中a
和b
是不同的。 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 -> a1
, a1 -> a2
,..., an -> b
。 More generally a Thrist fab
is a list of f
s fa a1
, f a1 a2
, ..., f an b
. 更一般地说,
Thrist fab
是f
s fa a1
, f 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.