考虑一个自由箭头,唯一的任务是记住它是如何构建的。我想在箭头的参数上加上一些约束(e.x. Show):
data FreeArrow a b where
LeafArrow :: (Show a, Show b) => FreeArrow a b
SeqArrow :: (Show a, Show b, Show c) => FreeArrow a b -> FreeArrow b c -> FreeArrow a c
ParArrow :: (Show a1, Show b1, Show a2, Show b2) => FreeArrow a1 b1 -> FreeArrow a2 b2 -> FreeArrow (a1, a2) (b1, b2)
deriving instance Show (FreeArrow a b)
要为FreeArrow定义Arrow实例,我们需要定义Category和Arrow的受限版本:
class Category cat where
id :: Show a => cat a a
(.) :: (Show a, Show b, Show c) => cat b c -> cat a b -> cat a c
class Category a => Arrow a where
arr :: (Show b, Show c) => (b -> c) -> a b c
first :: (Show b, Show c, Show d) => a b c -> a (b,d) (c,d)
second :: (Show b, Show c, Show d) => a b c -> a (d,b) (d,c)
(***) :: (Show b, Show c, Show b', Show c') => a b c -> a b' c' -> a
(&&&) :: (Show b, Show c, Show c') => a b c -> a b c' -> a b (c,c')
有些例子:
instance Category FreeArrow where
id = LeafArrow
(.) = flip SeqArrow
instance Arrow FreeArrow where
arr _ = LeafArrow
first f = ParArrow f id
second f = ParArrow id f
(***) = ParArrow
该方法适用于手写箭头:
myArrow :: FreeArrow Double Double
myArrow = (a &&& a) >>> arr (uncurry (+))
where a :: FreeArrow Double Double
a = LeafArrow
main :: IO ()
main = print myArrow
将打印:
SeqArrow (SeqArrow LeafArrow (ParArrow LeafArrow LeafArrow)) LeafArrow
但是对于箭头语法(GHC 7.8.3):
myArrow :: FreeArrow Double Double
myArrow = proc v -> a -< v
where a :: FreeArrow Double Double
a = LeafArrow
main :: IO ()
main = print myArrow
我收到的错误如下:
/home/ncrashed/dev/haskell/ArrowProblem.hs:54:11:
No instance for (Show c) arising from a use of ‘arr’
Possible fix:
add (Show c) to the context of
a type expected by the context: (b -> c) -> FreeArrow b c
In the expression: arr
When checking that ‘arr’ (needed by a syntactic construct)
has the required type: forall b1 c1. (b1 -> c1) -> FreeArrow b1 c1
arising from a proc expression
at /home/ncrashed/dev/haskell/ArrowProblem.hs:1:1
In the expression: proc v -> a -< v
/home/ncrashed/dev/haskell/ArrowProblem.hs:54:11:
No instance for (Show c) arising from a use of ‘>>>’
Possible fix:
add (Show c) to the context of
a type expected by the context:
FreeArrow a b -> FreeArrow b c -> FreeArrow a c
In the expression: (>>>)
When checking that ‘(>>>)’ (needed by a syntactic construct)
has the required type: forall a1 b1 c1.
FreeArrow a1 b1 -> FreeArrow b1 c1 -> FreeArrow a1 c1
arising from a proc expression
at /home/ncrashed/dev/haskell/ArrowProblem.hs:1:1
In the expression: proc v -> a -< v
/home/ncrashed/dev/haskell/ArrowProblem.hs:54:11:
No instance for (Show d) arising from a use of ‘first’
Possible fix:
add (Show d) to the context of
a type expected by the context:
FreeArrow b c -> FreeArrow (b, d) (c, d)
In the expression: first
When checking that ‘first’ (needed by a syntactic construct)
has the required type: forall b1 c1 d1.
FreeArrow b1 c1 -> FreeArrow (b1, d1) (c1, d1)
arising from a proc expression
at /home/ncrashed/dev/haskell/ArrowProblem.hs:1:1
有什么方法可以解决这个问题吗?这是一个错误吗?
也许我应该回到arrowp预处理器......
P.S。 There是代码的完整示例
答案 0 :(得分:1)
当您使用Haskell的箭头符号时,它不会天真地将proc v -> x -< y
标记为文字文本arr (\v -> y) >>> x
(使用arr
和{>>>
{ {1}}是范围内的,但是它使用了它所期望的真值,有效地使它变得有用:
Control.Arrow.arr (\v -> y) Control.Arrow.>>> x
问题恰恰在于当你遇到问题时 - 让我们假装它是一个带有封闭门口的房子 - 你选择在隔壁建造自己的房子,你可以打开门非常好(定义你自己的Arrow
和Category
个实例),现在你正试图驾驶一辆遥控车停在另一所房子里,而且你很难因为门口看起来非常相似而感到困惑,所以为什么在这个房子的楼上没有一辆RC车,所有的命令都被无缝地重新路由到?答案是,您需要构建自己的RC汽车和控制器(Haskell源到源转换器)来完成该方法。
显然,更简单的方法就是把所有这些工作都扔掉,而不是把门把手放到另一所房子里。
让我总结一下Haskell类型类的墙和门是如何工作的。您的编程水平足够高,可以进行审核,所以如果这太简短,我会道歉。例如,对于Show
类型类,您可以为类型构造函数构造此Show
函数字典(show
,showsPrec
,showList
),并且这些函数可以使用来自类的类型参数的其他约束的其他函数。
编译器然后存储一个Show
字典,它将类型构造函数转换为一对:首先,类型参数的约束列表,第二,您实现的实际字典,可能使用函数从早期的约束。当编译器解析Show (MyType ParamA ParamB)
时,它会在MyType
字典中查找Show
,找到一个实例,验证ParamA
和ParamB
是否满足它们所具有的约束条件满足(获取这些类的辅助词典),并形成Show
函数的字典。希望这主要是回顾;如果没有,那就去研究一下Haskell类型类如何工作的教程。
这意味着您不在您正在撰写的构造函数中需要Show
。他们是建造者!除了将后者模式匹配的方式粘合在一起之外,他们不做任何事情 - 他们不需要函数show
或showsPrec
来做这些事情。没有他们你仍然可以写:
data FreeArrow a b where
LeafArrow :: FreeArrow a b
SeqArrow :: FreeArrow a t -> FreeArrow t b -> FreeArrow a b
ParArrow :: FreeArrow a1 b1 -> FreeArrow a2 b2 -> FreeArrow (a1, a2) (b1, b2)
deriving instance Show (FreeArrow a b)
instance Category FreeArrow where id = LeafArrow; (.) = flip SeqArrow
instance Arrow FreeArrow where
arr = const LeafArrow
first = flip ParArrow id
second = ParArrow id
(***) = ParArrow
事实上,您现在可以在任意箭头上使用show
,
*Main> show (LeafArrow :: FreeArrow () (IO String))
"LeafArrow"
然而,使用上面的代码,您甚至无法创建该值,因为LeafArrow
构造函数需要Show
IO String
实例,而*Main> :set -XArrows
*Main> print $ proc v -> (LeafArrow :: FreeArrow Double Double) -< v
SeqArrow LeafArrow (SeqArrow LeafArrow LeafArrow)
无法提供它。您想要的代码很容易实现:
Show x
我们可以做到这一切,因为你的&#34;它是如何构建的&#34;信息实际上并不使用Show x
类型类为其任何实例定义Show
实例。
我能解决的唯一原因&#34;上面的内容是你不能深入反省所涉及的参数 - 我认为你想从class TypeOf t where
-- the integer here is 10 for type-constructor application, otherwise it is
-- the precedence of the enclosing type-construction-operator.
typePrec :: Int -> t -> String -> String
typePrec _ t = (typeof t ++)
typeof :: t -> String
typeof t = typePrec 0 t "" -- starts at 0 if we have no other info.
instance TypeOf Int where typeof _ = "Int"
instance (TypeOf x) => TypeOf [x] where
typeof list = "[" ++ typeof (head list) ++ "]"
instance (TypeOf x, TypeOf y) => TypeOf (x, y) where
typeof x = "(" ++ typeof (fst x) ++ ", " ++ typeof (snd x) ++ ")"
-- Some helper functions for precedence parsing:
arg f x = typePrec 10 (f x)
pIf m n expr | m <= n = ('(' :) . expr . (')' :)
| otherwise = expr
a <**> b = a . (' ' :) . b
infixr 1 <**>
instance (TypeOf x) => TypeOf (IO x) where
typePrec n x = pIf 10 n $ ("IO" ++) <**> arg io x
where io = undefined :: IO x -> x
instance (TypeOf x, TypeOf y) => TypeOf (Either x y) where
typePrec n x = pIf 10 n $ ("Either" ++) <**> arg left x <**> arg right x
where left = undefined :: Either x y -> x
right = undefined :: Either x y -> y
类型类中得到什么。实际上,你需要定义一个完全不同的类型类来从Haskell运行时获得这种类型的信息(除非我完全弄错了):
typeof :: x -> String
首先,可能有一种方法可以向Haskell写一个基于C的扩展,而不是为所有x提供(.)
到Haskell;然后你可以假设只需在箭头中添加一个字符串参数。让我们跳过这种可能性。
上面最大的问题是来自Control.Category
的{{1}},它在使用b
撰写cat a b
时明确禁止访问内部类型cat b c
。克服这一点将非常困难。除非您可以保护cat
不要更改类型,否则您无法通过cat
处理任何虚拟类型数据。
第二个问题是,您确实尝试使用元数据注释现有箭头,而不是定义自己的箭头来探测结构。沿着这些方向,你可能会看到这条疯狂的道路:
Prelude Control.Arrow Control.Monad.Free Control.Monad.Identity> :set prompt "ghci> "
ghci> type MyArrow = Kleisli Identity
ghci> let x = arr (3*) :: Kleisli (Free (ArrowMonad MyArrow)) Int Int
ghci> :t x
x :: Kleisli (Free (ArrowMonad MyArrow)) Int Int
基本上我们在这里寻找的是以某种方式使用typeof
&#34; walk&#34;这里的树由Free
monad体现。
有明显的限制阻止我们说&#34;如果它的输入和输出都实现TypeOf
&#34;这只是一个箭头。 (即arr
将不具有所需的TypeOf
约束),因此这些类型不能作为构造函数中的字符串隐藏;它们也不能隐藏在幻像类型中,因为它表示cat b c -> cat a b -> cat a c
,这是存储中间类型的唯一两种显而易见的方法。但是,据我所知,在构建我们的价值观结束时,有一个类是由同时实现Arrow
和TypeOf
的东西构建的,没有任何明显的限制。