haskell hlist hnil模式匹配hfoldl

时间:2014-04-11 04:05:32

标签: haskell

我一直试图让一些代码与Data.HList一起使用。我知道我可以单独使用ADT做我需要的东西,但我想看看它如何与HList一起工作,所以我正在试验。但是我在编写我编写的代码时遇到了问题。

{-# LANGUAGE GADTs #-}

module TestHList where

import Data.HList.CommonMain

data MyType1 = MyType1 { x::Int, y::Int } deriving (Show)
data MyType2 = MyType2 { text::String, slen::Int } deriving (Show)
data MyType3 = MyType3 { dval1::Int, dval2::String } deriving (Show)

test1 = HCons (MyType2 { text = "Hello", slen=5 })
          (HCons (MyType1 { x=1, y=2 })
          (HCons (MyType3 { dval1=3, dval2="World" })
          HNil))

test2 = HCons (MyType1 { x=4, y=5 })
          (HCons (MyType1 { x=6, y=7 })
          (HCons (MyType2 { text="Again.", slen=6 })
          HNil))

addType1 ls1 ls2 = hAppendList ls1 ls2


class MyTypesInt a where
  sumIt :: a -> Int

instance MyTypesInt MyType1 where
  sumIt val = (x val) + (y val)

instance MyTypesInt MyType2 where
  sumIt val = slen val

instance MyTypesInt MyType3 where
  sumIt val = (dval1 val) * 2

sumTest1 v = sumIt v
sumTest2 ls = sumIt (hHead ls)

foldTest ls = hFoldl (\(v1,v2) -> v1 + (sumIt v2)) 0 ls
sumTest3 = foldTest test1

sumAll HNil = 0
sumAll ls = (sumIt (hHead ls)) + (sumAll (hTail ls))

{-
sumAll3 xs
  | xs == HNil = 0
  | otherwise = (sumIt (hHead xs)) + (sumAll3 (hTail xs))
-}

代码没有做任何有用的事情,它只是为了帮助我理解如何使用HList。该代码声明了3种不同的数据类型,并创建了一个类并定义了3种类型的实例。我的目标是设置一个列表,然后根据为它们定义的实例对列表的每个元素执行类函数sumIt。我知道test1,test2 addType1,sumTest1和sumTest2工作。我得到的编译错误是针对foldTest和sumAll函数的。我想我需要定义函数声明但不确定如何。这是编译错误。

TestHList.hs:39:1:
Could not deduce (MyTypesInt a0)
  arising from the ambiguity check for `foldTest'
from the context (Num z,
                  HFoldl ((Int, a) -> Int) z xs r,
                  MyTypesInt a)
  bound by the inferred type for `foldTest':
             (Num z, HFoldl ((Int, a) -> Int) z xs r, MyTypesInt a) =>
             HList xs -> r
  at TestHList.hs:39:1-55
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
  instance MyTypesInt MyType3 -- Defined at TestHList.hs:33:10
  instance MyTypesInt MyType2 -- Defined at TestHList.hs:30:10
  instance MyTypesInt MyType1 -- Defined at TestHList.hs:27:10
When checking that `foldTest'
  has the inferred type `forall z (xs :: [*]) r a.
                         (Num z, HFoldl ((Int, a) -> Int) z xs r, MyTypesInt a) =>
                         HList xs -> r'
Probable cause: the inferred type is ambiguous

TestHList.hs:42:8:
Couldn't match type `(':) * e0 l0' with '[] *
Inaccessible code in
  a pattern with constructor
    HNil :: HList ('[] *),
  in an equation for `sumAll'
In the pattern: HNil
In an equation for `sumAll': sumAll HNil = 0

TestHList.hs:43:49:
Occurs check: cannot construct the infinite type: l0 = (':) * e0 l0
Expected type: HList ((':) * e0 ((':) * e0 l0))
  Actual type: HList ((':) * e0 l0)
In the first argument of `hTail', namely `ls'
In the first argument of `sumAll', namely `(hTail ls)'
In the second argument of `(+)', namely `(sumAll (hTail ls))'

我的问题是,是否有人知道我需要做些什么来修复代码以便它可以工作?我已经完成了不少搜索才能找到答案。我可以在搜索过程中看到答案,但我只是不理解它。

由于

更新

在研究我给出的答案中的想法时,我遇到了这个链接:http://en.wikibooks.org/wiki/Haskell/Existentially_quantified_types

阅读本文之后,很容易实现我想要做的事情。我没有改变答案。我的问题是关于如何让我的代码与Data.HList一起使用,并且提供的答案非常好。但我的目的是弄清楚如何设置和使用异构列表,我想当时Data.HList就是这样做的。以下代码对我来说更容易理解,所以我想提供它以防其他人发现它有用。

{-# LANGUAGE ExistentialQuantification #-}

module TestHeterList where

data MyType1 = MyType1 { x::Int, y::Int } deriving (Show)
data MyType2 = MyType2 { text::String, slen::Int } deriving (Show)
data MyType3 = MyType3 { dval1::Int, dval2::String } deriving (Show)

class MyTypesInt a where
  sumIt :: a -> Int

instance MyTypesInt MyType1 where
  sumIt val = (x val) + (y val)

instance MyTypesInt MyType2 where
  sumIt val = slen val

instance MyTypesInt MyType3 where
  sumIt val = (dval1 val) * 2

data GenElem = forall s. (Show s, MyTypesInt s) => GE s
instance Show GenElem where
  show (GE s) = show s

test1 :: [GenElem]
test1 = [GE (MyType2 { text = "Hello", slen=5 }), GE (MyType1 { x=1, y=2 }), GE (MyType3 { dval1=3, dval2="World" })]

foldTest xs = foldl (\acc (GE val) -> acc + sumIt val) (0::Int) xs
sumTest1 = foldTest test1

sumAll [] = 0
sumAll (GE v : xs) = (sumIt v) + (sumAll xs)

sumTest2 = sumAll test1

2 个答案:

答案 0 :(得分:6)

以下是基于hFoldl的变体的工作原理:

data HSumAll = HSumAll
instance (MyTypesInt a, int ~ Int) => ApplyAB HSumAll (Int, a) int where
  applyAB HSumAll (v1, v2) = v1 + sumIt v2 

foldTest ls = hFoldl HSumAll (0 :: Int) ls
sumTest3 = foldTest test1

使直接版本工作更棘手。首先,您必须使用模式匹配,因为HList是GADT,如果您使用选择器函数,则类型细化可能无法工作。此外,在GADT上匹配的函数需要显式类型签名。所以你最终得到这样的东西:

sumAll :: HList ls -> Int -- WRONG
sumAll HNil         = 0
sumAll (HCons x xs) = sumIt x + sumAll xs

这会产生以下类型错误:

  

无法推断使用(MyTypesInt e)引起的`sumIt'   来自上下文(ls ~ (':) * e l1)   ...

GHC当然是抱怨的权利。我们需要所有 ls中的类型作为实例 MyTypesInt。我浏览了HList包,看看该库是否提供了一种方法 表达这一点,但在我看来它并没有。幸运的是,这相对容易 这些天(需要ConstraintKinds并导入GHC.Exts才能访问Constraint):

type family All (c :: * -> Constraint) (xs :: [*]) :: Constraint
type instance All c '[]       = ()
type instance All c (x ': xs) = (c x, All c xs)

然后你可以说:

sumAll :: All MyTypesInt ls => HList ls -> Int
sumAll HNil         = 0
sumAll (HCons x xs) = sumIt x + sumAll xs

这种类型检查并按预期工作。

答案 1 :(得分:3)

使用hFoldl的问题在于,您正在折叠的函数不具有多态性,无法对MyType1MyType2MyType3类型的参数进行操作一切都在同一时间。请考虑以下简化示例:

testString = hFoldl f "" ((1 :: Int) `HCons` () `HCons` HNil)
  where
    f :: Show a => (String, a) -> String
    f = \(str, x) -> str ++ show x

尽管您可能认为这样可行,但由于Int()都可以show n,因此f未实例化,因为(String, Int) -> String已实例化将()应用于第一个元素时,HList显然无法应用于HList

{-# LANGUAGE GADTs, ScopedTypeVariables, FlexibleContexts, RankNTypes, TypeOperators, DataKinds #-} data SomeMyType = forall a. MyTypesInt a => SMT a testFold :: forall as xs . (SameLength' as xs, SameLength' xs as, HMapAux (Fun MyTypesInt SomeMyType) as xs, HFoldl ((Int, SomeMyType) -> Int) Int xs Int) => HList as -> Int testFold = foldHidden . hide where foldHidden :: HList xs -> Int foldHidden ls = hFoldl (\(v1,SMT v2) -> v1 + (sumIt v2)) (0 :: Int) ls hide :: HList as -> HList xs hide = hMap (Fun SMT :: Fun MyTypesInt SomeMyType) sumTest1 = testFold test1 可能有一些类型类折叠sumAll s具有多态函数,但我不太了解它,所以简单的解决方案是首先在输入列表上映射一个隐藏实际类型的存在类型构造函数,然后将折叠应用于现在同类型的列表:

HList xs

这可能并不令人满意,因为你必须定义一个新类型。

另一个功能更简单。首先,关于它为什么不起作用:您可以为class SumAll xs where sumAll :: HList xs -> Int instance SumAll '[] where sumAll HNil = 0 instance (MyTypesInt x, SumAll xs) => SumAll (x ': xs) where sumAll (HCons x xs) = sumIt x + sumAll xs sumTest2 = sumAll test1 分配什么类型?您无法指定合理类型,因为输入testFold必须是任意长度的列表。为此,您可以编写一个简单的类:

{{1}}

当然这只是{{1}}的特定版本。