如何在 Haskell 中映射具有多个约束的存在?

Dan*_*kov 6 haskell

我弄清楚了如何处理受单个类约束的异构类型列表:

\n
data Ex c = forall a. (c a) => Ex a\n\nforEx :: [Ex c] -> (forall a. c a => a -> b) -> [b] \nforEx [] _ = [] \nforEx (Ex a:r) f = f a : forEx r f\n\n> forEx @Show [Ex 3, Ex (), Ex True] show\n["3","()","True"]\n
Run Code Online (Sandbox Code Playgroud)\n

看起来不错,但在现实生活中,与 show 函数相比,它会更复杂,依赖于 1 个以上的约束。\n进一步类推是行不通的:

\n

尝试1:

\n
forEx @(Show, Eq) [Ex 3, Ex (), Ex True] show\n\n<interactive>:85:8: error:\n    \xe2\x80\xa2 Expected kind \xe2\x80\x98* -> Constraint\xe2\x80\x99, but \xe2\x80\x98(Show, Eq)\xe2\x80\x99 has kind \xe2\x80\x98*\xe2\x80\x99\n
Run Code Online (Sandbox Code Playgroud)\n

尝试2:

\n
type ShowEq c = (Show c, Eq c)\n> forEx @ShowEq [Ex 3, Ex (), Ex True] show\n\n<interactive>:87:1: error:\n    \xe2\x80\xa2 The type synonym \xe2\x80\x98ShowEq\xe2\x80\x99 should have 1 argument, but has been given none\n
Run Code Online (Sandbox Code Playgroud)\n

尝试 3 可行,但定义一次性使用的虚拟类和实例很笨拙:

\n
class (Show a, Eq a) => ShowEq1 a \ninstance (Show a, Eq a) => ShowEq1 a\nforEx @ShowEq1 [Ex (3::Int), Ex (), Ex True] show\n\n["3","()","True"]\n
Run Code Online (Sandbox Code Playgroud)\n

Ben*_*Ben 2

我会这样做:

\n
{-# LANGUAGE DataKinds, GADTs, RankNTypes, StandaloneKindSignatures, TypeFamilies, TypeOperators #-}\n\nimport Data.Kind\n\ntype All :: [Type -> Constraint] -> Type -> Constraint\ntype family All cs t\n  where All '[] _ = ()\n        All (c ': cs) t = (c t, All cs t)\n\ndata Ex cs\n  where Ex :: All cs t => t -> Ex cs\n\nforEx :: [Ex cs] -> (forall a. All cs a => a -> b) -> [b]\nforEx [] _ = []\nforEx (Ex x : xs) f = f x : forEx xs f\n
Run Code Online (Sandbox Code Playgroud)\n

现在Ex参数化的不是单个类,而是类列表1。我们有All类型族,用于获取类列表并将它们全部应用于同一类型,将它们组合成一个Constraint.

\n

这意味着(与类组合两个其他类的方法不同)它现在支持您需要的任意数量的约束。

\n

你可以这样称呼它2

\n
\xce\xbb forEx @[Show, Eq] [Ex 3, Ex (), Ex True] show\n["3","()","True"]\nit :: [String]\n
Run Code Online (Sandbox Code Playgroud)\n
\n

1从技术上讲,这不是一个类列表,而是一个需要多一个类型参数才能生成Constraint. 不过,单参数类将是您最常用的东西。

\n
\n

2当然,这并不是一个非常有用的约束,因为如果没有相同未知类型的另一个Eq值,您实际上无法使用它,并且您已经丢弃了有关任何给定类型是否相同类型的所有信息。

\n