带约束的箭头的可重新绑定语法
Rebindable syntax for arrow with constraint
考虑一个自由箭头,唯一的任务就是记住它是如何构造的。我想对箭头的参数施加一些约束(e.x。显示):
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 是代码的完整示例
问题是什么
当您使用 Haskell 的箭头表示法时,它不会天真地将 proc v -> x -< y
脱糖为 文字 文本 arr (\v -> y) >>> x
(使用任何arr
和 >>>
在范围内)而是它使用它期望的 真实值 ,有效地将其脱糖:
Control.Arrow.arr (\v -> y) Control.Arrow.>>> x
问题恰恰是,当您 运行 遇到问题时 - 让我们假装它是一栋门关着的房子 - 您选择在隔壁建造自己的房子,您的门可以完美打开好吧(定义你自己的 Arrow
和 Category
实例),现在你正试图驾驶一辆无可救药地卡在另一所房子里的遥控车,你很困惑,因为门口看起来非常相似,那么为什么 这个 房子楼上没有遥控车,你的所有命令都无缝地重新路由到?答案是,您需要构建自己的遥控车和控制器(Haskell 源到源转换器)才能完成该方法。
显然,更简单的方法是放弃所有这些工作,而是寻找通往另一所房子的门把手。
如何修复代码
让我总结一下 Haskell 类型 class 的墙和门是如何工作的。您的编程水平足够高,可能需要审核,所以如果这太简短,我深表歉意。例如,对于 Show
类型 class,您构造此 Show
函数字典 (show
、showsPrec
、showList
)类型构造函数,这些函数可以使用来自 class 类型参数的其他约束的其他函数。
编译器然后存储一个 Show
字典,它将类型构造函数配对:首先,类型参数的约束列表,其次,您已经实现的实际字典,可能使用函数从早期的限制。当编译器解析 Show (MyType ParamA ParamB)
时,它会在 Show
字典中查找 MyType
,找到一个实例,验证 ParamA
和 ParamB
是否满足它们必须满足的任何约束满足(为那些 classes 获取辅助函数字典),并形成 Show
函数的字典。希望这主要是回顾;如果没有,请学习有关 Haskell 类型 class 的工作原理的教程。
这意味着您不需要在您编写的构造函数中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
构造函数要求 IO String
的 Show
实例无法提供。而且您的预期代码可以轻松运行:
*Main> :set -XArrows
*Main> print $ proc v -> (LeafArrow :: FreeArrow Double Double) -< v
SeqArrow LeafArrow (SeqArrow LeafArrow LeafArrow)
我们可以做到所有这些,因为您的 "how it was constructed" 信息实际上并未使用 Show x
类型 class 来为其任何实例定义 Show x
实例。
如何做我认为你想做的事
我可以 "fix" 上述内容的唯一原因是你没有深入反省所涉及的参数——我认为这就是你想要的 Show
类型 class。事实上,您需要定义一个完全不同的类型 class 才能从 Haskell 运行时获取此类类型信息(除非我完全弄错了):
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 编写基于 C 的扩展,它为 Haskell 的所有 x 提供 typeof :: x -> String
;那么你可以假设只需向箭头添加一个字符串参数就可以了。让我们跳过这种可能性。
上面最大的问题是 Control.Category
中的 (.)
,它明确禁止在将 cat a b
与 cat b c
组合时访问内部类型 b
。克服这一点将非常困难。除非您可以保护 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
以某种方式 "walk" 这里由 Free
monad 体现的树。
有明显的限制阻止我们说 "this is only an arrow if both its input and its output implement TypeOf
"(即,arr
将没有所需的 TypeOf
约束),因此这些类型不能隐藏为字符串在构造函数中;它们也不能隐藏在幻像类型中,因为它表示 cat b c -> cat a b -> cat a c
,这是存储中间类型的仅有的两种明显方式)。但是,据我所知,没有任何明显的限制 class ,在构建我们的价值观的最后,它是由同时实现 Arrow
和 [=54= 的事物构建的].
考虑一个自由箭头,唯一的任务就是记住它是如何构造的。我想对箭头的参数施加一些约束(e.x。显示):
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 是代码的完整示例
问题是什么
当您使用 Haskell 的箭头表示法时,它不会天真地将 proc v -> x -< y
脱糖为 文字 文本 arr (\v -> y) >>> x
(使用任何arr
和 >>>
在范围内)而是它使用它期望的 真实值 ,有效地将其脱糖:
Control.Arrow.arr (\v -> y) Control.Arrow.>>> x
问题恰恰是,当您 运行 遇到问题时 - 让我们假装它是一栋门关着的房子 - 您选择在隔壁建造自己的房子,您的门可以完美打开好吧(定义你自己的 Arrow
和 Category
实例),现在你正试图驾驶一辆无可救药地卡在另一所房子里的遥控车,你很困惑,因为门口看起来非常相似,那么为什么 这个 房子楼上没有遥控车,你的所有命令都无缝地重新路由到?答案是,您需要构建自己的遥控车和控制器(Haskell 源到源转换器)才能完成该方法。
显然,更简单的方法是放弃所有这些工作,而是寻找通往另一所房子的门把手。
如何修复代码
让我总结一下 Haskell 类型 class 的墙和门是如何工作的。您的编程水平足够高,可能需要审核,所以如果这太简短,我深表歉意。例如,对于 Show
类型 class,您构造此 Show
函数字典 (show
、showsPrec
、showList
)类型构造函数,这些函数可以使用来自 class 类型参数的其他约束的其他函数。
编译器然后存储一个 Show
字典,它将类型构造函数配对:首先,类型参数的约束列表,其次,您已经实现的实际字典,可能使用函数从早期的限制。当编译器解析 Show (MyType ParamA ParamB)
时,它会在 Show
字典中查找 MyType
,找到一个实例,验证 ParamA
和 ParamB
是否满足它们必须满足的任何约束满足(为那些 classes 获取辅助函数字典),并形成 Show
函数的字典。希望这主要是回顾;如果没有,请学习有关 Haskell 类型 class 的工作原理的教程。
这意味着您不需要在您编写的构造函数中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
构造函数要求 IO String
的 Show
实例无法提供。而且您的预期代码可以轻松运行:
*Main> :set -XArrows
*Main> print $ proc v -> (LeafArrow :: FreeArrow Double Double) -< v
SeqArrow LeafArrow (SeqArrow LeafArrow LeafArrow)
我们可以做到所有这些,因为您的 "how it was constructed" 信息实际上并未使用 Show x
类型 class 来为其任何实例定义 Show x
实例。
如何做我认为你想做的事
我可以 "fix" 上述内容的唯一原因是你没有深入反省所涉及的参数——我认为这就是你想要的 Show
类型 class。事实上,您需要定义一个完全不同的类型 class 才能从 Haskell 运行时获取此类类型信息(除非我完全弄错了):
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 编写基于 C 的扩展,它为 Haskell 的所有 x 提供 typeof :: x -> String
;那么你可以假设只需向箭头添加一个字符串参数就可以了。让我们跳过这种可能性。
上面最大的问题是 Control.Category
中的 (.)
,它明确禁止在将 cat a b
与 cat b c
组合时访问内部类型 b
。克服这一点将非常困难。除非您可以保护 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
以某种方式 "walk" 这里由 Free
monad 体现的树。
有明显的限制阻止我们说 "this is only an arrow if both its input and its output implement TypeOf
"(即,arr
将没有所需的 TypeOf
约束),因此这些类型不能隐藏为字符串在构造函数中;它们也不能隐藏在幻像类型中,因为它表示 cat b c -> cat a b -> cat a c
,这是存储中间类型的仅有的两种明显方式)。但是,据我所知,没有任何明显的限制 class ,在构建我们的价值观的最后,它是由同时实现 Arrow
和 [=54= 的事物构建的].