Haskell中单体的还原
假设我有一个类型定义为:Haskell中单体的还原,haskell,monads,monad-transformers,Haskell,Monads,Monad Transformers,假设我有一个类型定义为: data Node = forall a b. Node (SimpleWire a b) SimpleWire是一个monad,其中a表示输入,b表示输出。我可以在那个单子上做函数交换。因此,假设我有SimpleWire A B类型的wireA,和SimpleWire B C类型的wireB,执行wireA。wireB会给我一个C的类型 现在,我想折叠该单子的列表(对于本例,其类型为[Node])。比如: buildGraph :: [Node] -> (Si
data Node = forall a b. Node (SimpleWire a b)
SimpleWire
是一个monad,其中a
表示输入,b
表示输出。我可以在那个单子上做函数交换。因此,假设我有SimpleWire A B
类型的wireA
,和SimpleWire B C
类型的wireB
,执行wireA。wireB
会给我一个C的类型
现在,我想折叠该单子的列表(对于本例,其类型为[Node]
)。比如:
buildGraph :: [Node] -> (SimpleWire a b)
buildGraph (Node h):t = h . (buildGraph t)
如何使此代码在Haskell的类型系统中工作?我们不能用建议的类型组合
[Node]
。这是因为否则我们会
sw1 :: SimpleWire A B
sw2 :: SimpleWire C D
buildGraph :: [Node] -> (SimpleWire a b)
buildGraph [ sw1, sw2 ] :: SimpleWire E F
这太强烈了。我们能够组合任意的、不兼容的类型(错误),然后在最后随机写入(错误)
问题是我们丢失了[Node]
类型中的所有类型信息。我们需要记住一些,即:
data NodeList a b where
Nil :: NodeList a a
Cons :: Node a b -> NodeList b c -> NodeList a c
然后
buildGraph :: NodeList a b -> SimpleWire a b
buildGraph Nil = id
buildGraph (Cons (Node h) t) = h . buildGraph t
我将假设以下故事: 你可能用过那种类型
data Node = forall a b. Node (SimpleWire a b)
而不仅仅是SimpleWire a b
,因为您想要一个SimpleWire
的列表,其中a
和b
是不同的。特别是,作为buildGraph
的参数,您真正希望的是(在pseudo Haskell中)
但是,您无法用Haskell的标准同构[]
表达第一个列表,而是尝试使用通用量化类型来摆脱这种困境
如果我说的是真的,你可能在寻找。特别是,您可以完全取消节点
。A第三个(->)ab
是函数列表A->a1
,a1->a2
,…,A->b
。更一般地说,第三个fab
是f
sfaa1
,faa2
,…,fab
的列表
{-# 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)
旁注:
尽管SimpleWire
很可能是单子,但这可能不会直接帮助您。特别是当函数是单子时,您似乎关心的是对函数组合概念的概括,这是函数的用途(并且只与单子有间接关系)。我使用了>>
,并且Thrist
有一个箭头
实例,这一事实就暗示了这一点。正如我在代码注释中提到的,SimpleWire
可能是一个箭头
{-# 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)