4

给定以下课程:

class ListIsomorphic l where
    toList   :: l a -> [a]
    fromList :: [a] -> l a

如何使用 编写向量类型的实例Data.Vector.Generic?这不起作用:

instance (V.Vector v a) => ListIsomorphic v where
    toList   = V.toList
    fromList = V.fromList

给我:

test.hs:31:10:
    Variable ‘a’ occurs more often than in the instance head
      in the constraint: V.Vector v a
    (Use UndecidableInstances to permit this)
    In the instance declaration for ‘ListIsomorphic v’
4

2 回答 2

6

不要。由于重叠的实例,v为你的类添加一个实例将变得很麻烦。Listable

AVector v a => v不与列表同构,因为它受哪些项目可以是列表元素的限制。您需要一个捕获此约束的类,例如

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}

import Data.Constraint

class ConstrainedList l where
    type Elem l a :: Constraint
    toList   :: Elem l a => l a -> [a]
    fromList :: Elem l a => [a] -> l a

ConstrainedList我们不会为所有类型添加实例,Vector v a => v这会使我们进入重叠的实例领域,而是只为我们感兴趣的类型定义它。下面将涵盖Vector向量包中具有实例的所有类型。

import qualified Data.Vector.Primitive as VP
import qualified Data.Vector.Generic as VG

instance ConstrainedList VP.Vector where
    type Elem VP.Vector a = VG.Vector VP.Vector a
    toList   = VG.toList
    fromList = VG.fromList

其他类型的实例

您可以为其元素只需要空约束的ConstrainedList常规列表编写一个实例。[]

instance ConstrainedList [] where
    type Elem [] a = ()
    toList   = id
    fromList = id

任何使用toListfromList也需要Elem l a实例的地方。

cmap :: (ConstrainedList l, Elem l a, Elem l b) => (a -> b) -> l a -> l b
cmap f = fromList . map f . toList

当我们知道列表和元素的具体类型时,这些函数将很容易使用,而不会受到约束。

cmap (+1) [1,2,3,4]

这里是龙

不要尝试以下内容。如果您对与没有额外约束的列表同构的事物类感兴趣,只需为它创建另一个类。这只是展示了当你将自己设计到一个角落时你可以做什么:召唤一条龙。

您还可以编写需要证明对 a 的元素没有约束的函数ConstrainedList。这constraints与 GHC 不真正支持的包和编程风格领域相去甚远,但是没有足够的constraints示例,所以我将把这个留在这里。

{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}

map' :: forall l a b. (ConstrainedList l, () :=> Elem l a, () :=> Elem l b) =>
                      (a -> b) -> l a -> l b
map' f = case (ins :: () :- Elem l a) of { Sub Dict ->
         case (ins :: () :- Elem l b) of { Sub Dict ->
         fromList . map f . toList
         }}

我们可以ConstrainedList通过检查 a 来检查 a 是否没有约束Elem l a ~ (),但是如果它的约束以不同的方式编写,那将不起作用。

{-# LANGUAGE FlexibleInstances #-}

class Any a
instance Any a

data AList a = AList {getList :: [a]}
    deriving (Show)

instance ConstrainedList AList where
    type Elem AList a = Any a
    toList   = getList
    fromList = AList

()Any a即使()隐含的类型不同Any a。约束包通过将它们具体化为类型类来捕获这样的关系,Class:=>

{-# LANGUAGE MultiParamTypeClasses #-}

--       class () => Any a
instance Class ()   (Any a) where
    cls = Sub Dict

-- instance ()  => Any a
instance    () :=> Any a where
    ins = Sub Dict

所有这些工作让我们可以轻松地重用函数,而无需在已知具体列表类型时提供所有这些字典。

map'' :: (a -> b) -> AList a -> AList b
map'' = map'
于 2015-08-20T23:12:53.367 回答
5

我经常遇到这个问题。以下是我提出的两个解决方案:

  1. 更改类参数:

    class ListIsomorphic l a where
      toList :: l a -> [a]
      fromList :: [a] -> l a
    
    instance (V.Vector v a) => Listable v a where
      ...
    
  2. 使用约束类型

    class ListIsomorphic l where
      type C l a :: Constraint
      toList :: l a -> [a]
      fromList :: [a] -> l a
    
    instance Listable v where
      type C v a = (V.Vector v a)
      ...
    
于 2015-08-20T23:04:37.750 回答