Haskell类型族和伪参数

时间:2010-01-11 17:35:37

标签: haskell type-families

我的功能类似于numpy的array。它将列表转换为数组,将列表列表转换为2d数组等。

它的工作原理如下:

ghci> arrFromNestedLists ["hello", "world"] :: Array (Int, (Int, ())) Char
array ((0,(0,())),(1,(4,()))) [((0,(0,())),'h'),((0,(1,())),'e'),((0,(2,())),'l'),((0,(3,())),'l'),((0,(4,())),'o'),((1,(0,())),'w'),((1,(1,())),'o'),((1,(2,())),'r'),((1,(3,())),'l'),((1,(4,())),'d')]

(Int, (Int, ()))而非(Int, Int)因为我不知道增加元组长度的程序方法。 (旁边的问题:有这样的方式吗?)

它的编码很尴尬,我不得不做一个“解决方法”(将伪参数传递给函数)以使其工作。我想知道是否有更好的方法。

所以这里是代码,中断了丑陋的变通方法的细节:

{-# LANGUAGE FlexibleInstances, ScopedTypeVariables, TypeFamilies #-}

type family ListOfIndex i a
type instance ListOfIndex () a = a
type instance ListOfIndex (Int, i) a = [ListOfIndex i a]

class Ix i => ArrConv i where
  acBounds :: a -> ListOfIndex i a -> (i, i)
  acFlatten :: i -> ListOfIndex i a -> [a]

acBounds“应该”为:: ListOfIndex i a -> (i, i)。同样适用于acFlatten。每个都给出一个虚拟变量(undefined始终是给定的值)因为否则我无法编译它:(

arrFromNestedLists :: forall i a. ArrConv i => ListOfIndex i a -> Array i a
arrFromNestedLists lst =
  listArray
  (acBounds (undefined :: a) lst)
  (acFlatten (undefined :: i) lst)

上面是在工作中传递的虚拟undefined参数。它告诉GHC使用哪个ListOfIndex实例。

instance ArrConv () where
  acBounds _ = const ((), ())
  acFlatten _ = (: [])

以下函数应该是acBounds实例中的ArrConv函数,并且只在外面声明,因为我需要使用ScopedTypeVariables而我不知道怎么做在实例定义中的函数中执行..

acSucBounds
  :: forall a i. ArrConv i
  => a -> [ListOfIndex i a] -> ((Int, i), (Int, i))
acSucBounds _ lst =
  ((0, inStart), (length lst - 1, inEnd))
  where
    (inStart, inEnd) = acBounds (undefined :: a) (head lst)

instance ArrConv i => ArrConv (Int, i) where
  acBounds = acSucBounds
  acFlatten _ = concatMap (acFlatten (undefined :: i))

2 个答案:

答案 0 :(得分:4)

acBounds和acFlatten的额外参数是必要的原因是ai类型无法分别从ListOfIndex i a -> (i, i)ListOfIndex i a -> [a]恢复。一种解决方法是将两种方法合并为acArgs类型的ListOfIndex i a -> ((i, i), a)方法。现在唯一的问题是在(Int, i)的实例中使用它,以防止类型检查器过多地推广其类型导致与以前相同的问题(例如,我们不能简单地使用fst . acArgs

{-# LANGUAGE TypeFamilies, FlexibleInstances #-}

import Data.Array

type family ListOfIndex i a
type instance ListOfIndex () a = a
type instance ListOfIndex (Int, i) a = [ListOfIndex i a]

class Ix i => ArrConv i where
  acArgs :: ListOfIndex i a -> ((i, i), [a])

instance ArrConv () where
  acArgs x = (((), ()), [x])

instance ArrConv i => ArrConv (Int, i) where
  acArgs lst =
    (((0, inStart), (length lst - 1, inEnd)), args >>= snd)
    where
      args = map acArgs lst
      (inStart, inEnd) = fst (head args)

arrFromNestedLists :: ArrConv i => ListOfIndex i a -> Array i a
arrFromNestedLists = uncurry listArray . acArgs

答案 1 :(得分:0)

如果您希望将acBoundsacFlatten分开,可以向其添加type-level tag argument,即acBounds的类型为acBounds :: Proxy a -> ListOfIndex i a -> (i, i)。这消除了对undefined参数的需要,因为您可以将(Proxy :: SomeConcreteType)传递给它;并且acBounds无法从中提取任何有用的值级别信息,因为它与单元类型是同构的(以无类型方式)。