Haskell 统一与类约束相关联的类型同义词

Haskell 统一与类约束相关联的类型同义词,haskell,type-families,Haskell,Type Families,我有一个嵌套类型,希望使用关联的类型同义词部分指定该类型。下面是一些非常精简的代码,演示了该问题: {-# LANGUAGE TypeFamilies, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses #-} f :: Int -> Int f x = x+1 data X t i newtype Z i = Z i deriving (Eq,Show) newtype Y q = Y (T q) class Qu

我有一个嵌套类型,希望使用关联的类型同义词部分指定该类型。下面是一些非常精简的代码,演示了该问题:

{-# LANGUAGE TypeFamilies,
FlexibleInstances,
FlexibleContexts,
MultiParamTypeClasses #-}

f :: Int -> Int
f x = x+1

data X t i
newtype Z i = Z i deriving (Eq,Show)
newtype Y q = Y (T q)

class Qux m r where
    g :: (T m) -> r

class (Integral (T a)) => Foo a where
    type T a
    -- ... functions

instance (Integral i) => Foo (X (Z i) i) where
    type T (X (Z i) i) = i

instance (Foo q) => Num (Y q) -- where ...

instance (Foo q, Foo (X m' Int)) => Qux (X m' Int) (Y q) where
    g x =  fromIntegral $ f $ x
哪种情况(即使是不可判定的实例)会导致编译器错误:

Could not deduce (T (X m' Int) ~ Int)
我知道将此约束添加到Qux实例会使编译器感到高兴。但是,我知道在我的程序中(T(X arg1 arg2))=arg2,所以我试图找出如何必须编写此约束

instance Foo (X (Z  i) i) where type T (X (Z  i) i) = Z  i
instance Foo (X (Z' i) i) where type T (X (Z' i) i) = Z' i
我的问题是:我能让Haskell意识到,当我将'Int'作为X的第二个参数时,这与同义词T(xa'Int)是一样的吗?我意识到我在使用有关实例外观的“特殊”知识,但我认为应该有一种方法将其传达给编译器


谢谢

因为我还不确定我是否理解这个问题,我将讨论您编写的代码;也许我的漫谈会为你指明一个有用的方向,或者引发一些我可以回答的尖锐问题。也就是说:警告!漫无边际的回答

首先,让我们谈谈
Bar

-- class (i ~ S b) => Bar b i where
--     type S b
--     ...
既然我们知道约束条件
i~sb
,我们不妨放弃
i
参数,我将在接下来的讨论中这样做

class Bar b where type S b
-- or, if the class is empty, just
-- type family S b
-- with no class declaration at all
下面是这个新类的实例的外观:

instance Bar (Z  i) where type S (Z  i) = i
instance Bar (Z' i) where type S (Z' i) = i

如果对于任何类型的构造函数来说都是这样的话,你可以考虑把这个写为一个实例,而不是:

-- instance Bar (f i) where type S (f i) = i
现在,让我们谈谈
Foo
类。要将其更改为与上述内容相匹配,我们将

class Bar (T a) => Foo a where type T a
您已经声明了两个实例:

-- instance (Bar (Z i) i) => Foo (X (Z i) i) where
--     type T (X (Z i) i) = Z i
--
-- instance (Bar (Z' i) i) => Foo (X' (Z' i) i) where
--     type T (X (Z' i) i) = Z' i
我们可以像以前一样将第二个参数剥离到
Bar
,但我们还可以做另一件事:因为我们知道有一个
Bar(zi)
实例(我们在上面声明了它!),所以我们可以去掉实例约束

instance Foo (X (Z  i) i) where type T (X (Z  i) i) = Z  i
instance Foo (X (Z' i) i) where type T (X (Z' i) i) = Z' i
是否将
i
参数保留为
X
取决于
X
应该表示什么。到目前为止,我们还没有更改任何类声明或数据类型的语义,只是更改了它们的声明和实例化方式。更改
X
可能不具有相同的属性;很难说没有看到
X
的定义。有了数据声明和足够多的扩展,上面的前奏曲就完成了

现在,您的投诉:

  • 您说以下内容无法编译:

    class Qux a
    instance Foo (X  a' Int) => Qux (X  a' Int)
    instance Foo (X' a' Int) => Qux (X' a' Int)
    
    但是,对于
    不可判定的实例
    ,它确实在这里编译。它需要
    不可判定的实例
    :没有什么可以阻止某人稍后出现并声明这样的实例

    instance Qux (X Y Int) => Foo (X Y Int)
    
    Then, checking whether `Qux (X Y Int)` had an instance would require checking whether `Foo (X Y Int)` had an instance and vice versa. Loop.
    
  • 您会说,“它还需要实例约束
    S(T(xa'))~Int
    ,尽管我知道在我的程序中,它们总是同义词。”。我不知道第一个“it”是什么——我不能重现这个错误——所以我不能很好地回答这个问题。另外,正如我以前抱怨的那样,这个约束是不恰当的:
    X:(*->*)->*->*->*
    ,因此
    xa':*->*
    ,而
    T
    需要一个
    *
    类型的参数。所以我假设你的意思是
    S(T(xa'Int))~Int

    尽管有这些抱怨,我们仍然可以问,为什么从我们目前的假设来看,
    S(T(xa'Int))~Int
    是不可证明的。到目前为止,我们只为符合模式
    X(zi)i
    X(zi)i
    的类型声明了
    Foo
    实例。因此,类型函数
    T
    只能在其参数类型符合其中一种模式时减少。类型
    xa'Int
    不太适合这两种模式。我们可以把它塞进正确的模式:我们可以试着用
    X(zint)Int
    来代替(比如)。然后我们会发现
    T(X(Z Int)Int)~Z Int
    ,因此
    S(T(X(Z Int)Int)~S(Z Int)~Int

    这回答了如何修复类型级别的缩减,但没有解释如何修复任何没有生成的代码。为此,我们必须找出类型检查器为什么需要从
    S(t(xa'Int))
    Int
    ,并看看我们是否能说服它,像
    S(t(X(Z Int))这样的更具体(且令人满意)的强制Int)~Int
    ,或者,使用上面建议的广义
    Bar
    实例,
    S(T(X(f Int)Int)~Int
    。如果没有足够的代码重现错误,我们当然无法帮助您

  • 你会问,“我能让Haskell意识到当我把'Int'作为X的第二个参数时,这与同义词S(T(xa'Int))是一样的吗?”。我根本不理解这个问题。你想要一个可证明的等式
    xaint~S(T(xa'Int))
    ?这就是我从你的问题的字面解读中得到的解释

    在上下文中,我想你可能想问你是否能得到一个可证明的等式
    b~S(T(xab))
    ;在这种情况下,答案是“肯定!”。我们将滥用我们在上面知道的
    b~S(zb)
    的事实,把这个等式化为更强的
    zb~T(xab)
    。然后我们可以将上面的
    Foo
    实例替换为一个进行此声明的实例,仅此而已:

    instance Foo (X a b) where T (X a b) = Z b
    
    或者,我们可以假设另一个合理的方程,
    T(xab)~a
    ;然后,为了证明
    S(T(xab))~b
    ,我们只需要证明
    sa~b
    (通过减少
    T
    ),我们就可以编写另一个
    Foo
    实例:

    instance (Bar a, S a ~ b) => Foo (X a b) where T (X a b) = a
    

我很难理解这一点。您是否向我们展示了
Qux
类声明?什么是“以相同方式嵌套的其他Foo实例”意思?什么是“根据X生成多个实例,但对于generiz Z和固定嵌套类型I”意思?此外,您提出的许多约束看起来不太好