具有约束的箭头的可重定义语法

时间:2015-06-04 11:21:02

标签: haskell arrows

考虑一个自由箭头,唯一的任务是记住它是如何构建的。我想在箭头的参数上加上一些约束(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是代码的完整示例

1 个答案:

答案 0 :(得分:1)

问题是什么

当您使用Haskell的箭头符号时,它不会天真地将proc v -> x -< y标记为文字文本arr (\v -> y) >>> x(使用arr和{>>> { {1}}是范围内的,但是它使用了它所期望的真值,有效地使它变得有用:

Control.Arrow.arr (\v -> y) Control.Arrow.>>> x

问题恰恰在于当你遇到问题时 - 让我们假装它是一个带有封闭门口的房子 - 你选择在隔壁建造自己的房子,你可以打开门非常好(定义你自己的ArrowCategory个实例),现在你正试图驾驶一辆遥控车停在另一所房子里,而且你很难因为门口看起来非常相似而感到困惑,所以为什么在这个房子的楼上没有一辆RC车,所有的命令都被无缝地重新路由到?答案是,您需要构建自己的RC汽车和控制器(Haskell源到源转换器)来完成该方法。

显然,更简单的方法就是把所有这些工作都扔掉,而不是把门把手放到另一所房子里。

如何修复代码

让我总结一下Haskell类型类的墙和门是如何工作的。您的编程水平足够高,可以进行审核,所以如果这太简短,我会道歉。例如,对于Show类型类,您可以为类型构造函数构造此Show函数字典(showshowsPrecshowList),并且这些函数可以使用来自类的类型参数的其他约束的其他函数。

编译器然后存储一个Show字典,它将类型构造函数转换为一对:首先,类型参数的约束列表,第二,您实现的实际字典,可能使用函数从早期的约束。当编译器解析Show (MyType ParamA ParamB)时,它会在MyType字典中查找Show,找到一个实例,验证ParamAParamB是否满足它们所具有的约束条件满足(获取这些类的辅助词典),并形成Show函数的字典。希望这主要是回顾;如果没有,那就去研究一下Haskell类型类如何工作的教程。

这意味着您在您正在撰写的构造函数中需要Show。他们是建造者!除了将后者模式匹配的方式粘合在一起之外,他们不做任何事情 - 他们不需要函数showshowsPrec来做这些事情。没有他们你仍然可以写:

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,这是存储中间类型的唯一两种显而易见的方法。但是,据我所知,在构建我们的价值观结束时,有一个类是由同时实现ArrowTypeOf的东西构建的,没有任何明显的限制。