Haskell 异构列表长度中的类型变量不明确

Haskell 异构列表长度中的类型变量不明确,haskell,Haskell,在计算异构列表的长度时,我遇到了类型变量不明确的问题。问题似乎是长度函数在HList的元素中不是多态的 我的代码 首先,我使用的所有语言扩展: {-# LANGUAGE DataKinds #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDep

在计算异构列表的长度时,我遇到了类型变量不明确的问题。问题似乎是长度函数在HList的元素中不是多态的

我的代码 首先,我使用的所有语言扩展:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
自然数的定义如下:

data Nat = Zero | Succ Nat

data HNat n where
    HZero :: HNat Zero
    HSucc :: HNat n -> HNat (Succ n)
异构列表被定义为GADT

data HList (ts :: [*]) where
    HNil  :: HList '[]
    (:::) :: t -> HList ts -> HList (t ': ts)
infixr 5 :::
我们可以查询HList的长度:

class HLength xs n | xs -> n where
    hLength :: HList xs -> HNat n

instance HLength '[] Zero where
    hLength HNil = HZero

instance HLength xs n => HLength (x ': xs) (Succ n) where
    hLength (_ ::: xs) = HSucc (hLength xs)
我们可以索引到HList并检索
i
-th元素:

class HIndex xs i y | xs i -> y where
    hIndex :: HList xs -> HNat i -> y

instance HIndex (x ': xs) Zero x where
    hIndex (x ::: xs) HZero = x

instance HIndex xs i y => HIndex (x ': xs) (Succ i) y where
    hIndex (x ::: xs) (HSucc i) = hIndex xs i
问题 有了这一点,我将演示这个问题

假设我构造了一个HList,其中包含一个函数,该函数本身接受一个HList,并使用它的第一个元素执行某些操作

test1 = ((\l n -> l `hIndex` HZero || n == 0) ::: HNil)
在这种情况下,第一个元素必须是Bool。派生类型签名确认该约束:

:: (Eq a, Num a, HIndex xs 'Zero Bool) =>
    HList '[HList xs -> a -> Bool]
现在我想计算列表的长度
test

test2 = hLength test1
不幸的是,这无法编译,并显示以下错误消息:

HListConstraints.hs:55:17:
    No instance for (HIndex xs0 'Zero Bool)
      arising from a use of ‘test1’
    The type variable ‘xs0’ is ambiguous
    Note: there is a potential instance available:
      instance HIndex (x : xs) 'Zero x
        -- Defined at HListConstraints.hs:42:10
    In the first argument of ‘hLength’, namely ‘test1’
    In the expression: hLength test1
    In an equation for ‘test2’: test2 = hLength test1
Failed, modules loaded: none.
列表元素上的约束会导致类型变量不明确


我的理解是,我需要在传递给它的列表元素中使
hLength
多态。我怎样才能做到这一点呢?

问题不在于
HLength
,它已经是多态的了。问题在于
HIndex
,它在
xs
参数中不必要地特定

HIndex xs'Zero Bool
中,我们应该能够推断
xs
具有
Bool':xs'
某些
xs'
的形状。由于
HIndex
实现是由
Nat
类参数驱动的,因此我们可以在实例头中保留未指定的其他参数,而是在实例约束中对其进行细化,从而使GHC能够进行上述推断:

class HIndex xs i y | xs i -> y where
    hIndex :: HList xs -> HNat i -> y

instance (xs ~ (x ': xs')) => HIndex xs Zero x where
    hIndex (x ::: xs) HZero = x

instance (xs ~ (y ': xs'), HIndex xs' i x) => HIndex xs (Succ i) x where
    hIndex (x ::: xs) (HSucc i) = hIndex xs i
在此之后:

test1 :: (Eq a, Num a) => HList '[HList (Bool : xs') -> a -> Bool] 
HIndex
约束消失,当
Num a
使
a
默认为
Integer
时,其余约束得到解决:

> :t hLength test1
hLength test1 :: HNat ('Succ 'Zero)

使用类型类进行计算时的一般规则是将类型依赖项移动到实例约束中,并且只在实例头中进行那些对实例定义至关重要的模式匹配。这将实例头匹配问题(当参数的形式不正确时会立即失败)转化为约束求解问题(可以根据程序其他部分的信息延迟求解)

或者,我们可以明确地将
test1
的类型定义为
test1::HList((HList(Bool):xs)->Int->Bool):'[])

这允许我们保持实例定义的原样,并消除András提出的
xs~(x':xs')
形式的类型相等约束

然后,您可以选择是否必须显式定义
test1
的类型。类型相等约束使定义
test1
成为可能,而无需提供类型注释。

test1::(Eq a,Num a)=>HList'[HList(Bool:xs')->a->Bool]
中的apotroph应该移动到
之前,以给出
HList(Bool):xs)
。我无法将此更改作为编辑应用,因为它太短。