Haskell 异构列表长度中的类型变量不明确
在计算异构列表的长度时,我遇到了类型变量不明确的问题。问题似乎是长度函数在HList的元素中不是多态的 我的代码 首先,我使用的所有语言扩展:Haskell 异构列表长度中的类型变量不明确,haskell,Haskell,在计算异构列表的长度时,我遇到了类型变量不明确的问题。问题似乎是长度函数在HList的元素中不是多态的 我的代码 首先,我使用的所有语言扩展: {-# LANGUAGE DataKinds #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDep
{-# 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)
。我无法将此更改作为编辑应用,因为它太短。