异构列表长度的模糊类型变量

时间:2016-05-07 19:04:17

标签: haskell

在计算异构列表的长度时,我遇到了模糊类型变量的问题。问题似乎是长度函数在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 - 元素:

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多态。 我该怎么做?

2 个答案:

答案 0 :(得分:4)

问题不在于HLength,而且它已经是多态的。问题出在HIndex,这在xs参数中是不必要的。

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

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)

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

答案 1 :(得分:0)

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

这使我们可以保留实例定义,并消除András提出的xs ~ (x ': xs')形式的类型相等约束。

然后选择是是否要明确定义test1的类型。使用类型相等约束可以定义test1而无需提供类型注释。