如何为通用向量创建 ListIsomorphic 实例?
How do I create a ListIsomorphic instance for generic vectors?
鉴于以下 class:
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’
我经常运行遇到这个问题。这是我想出的两个解决方案:
更改class参数:
class ListIsomorphic l a where
toList :: l a -> [a]
fromList :: [a] -> l a
instance (V.Vector v a) => Listable v a where
...
使用约束种类
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)
...
不要。将所有 v
的实例添加到您的 Listable
class 将由于实例重叠而变得难以使用。
A Vector v a => v
与列表不同构,因为它受哪些项目可以是列表元素的限制。你需要一个 class 来捕获这个约束,比如
{-# 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
我们不会为所有类型 Vector v a => v
添加 ConstrainedList
个实例,这会让我们进入重叠实例领域,相反,我们将只为我们感兴趣的类型定义它。以下将用矢量包中的 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
任何使用 toList
或 fromList
的地方也需要一个 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]
这里有龙
不要尝试以下内容。如果您对 class 与没有额外约束的列表同构的事物感兴趣,只需为其创建另一个 class。这只是展示了当你将自己设计到一个角落时你可以做什么:召唤一条龙。
您还可以编写需要证明对 ConstrainedList
的元素没有约束的函数。这与 GHC 并不真正支持的 constraints
包和编程风格的领域相去甚远,但是没有足够的 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
}}
我们可以通过检查 Elem l a ~ ()
来检查 ConstrainedList
是否没有约束,但是如果它的约束以不同的方式编写,那将不起作用。
{-# 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
。约束包通过将它们具体化为 classes Class
and :=>
类型来捕获这样的关系
{-# 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'
鉴于以下 class:
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’
我经常运行遇到这个问题。这是我想出的两个解决方案:
更改class参数:
class ListIsomorphic l a where toList :: l a -> [a] fromList :: [a] -> l a instance (V.Vector v a) => Listable v a where ...
使用约束种类
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) ...
不要。将所有 v
的实例添加到您的 Listable
class 将由于实例重叠而变得难以使用。
A Vector v a => v
与列表不同构,因为它受哪些项目可以是列表元素的限制。你需要一个 class 来捕获这个约束,比如
{-# 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
我们不会为所有类型 Vector v a => v
添加 ConstrainedList
个实例,这会让我们进入重叠实例领域,相反,我们将只为我们感兴趣的类型定义它。以下将用矢量包中的 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
任何使用 toList
或 fromList
的地方也需要一个 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]
这里有龙
不要尝试以下内容。如果您对 class 与没有额外约束的列表同构的事物感兴趣,只需为其创建另一个 class。这只是展示了当你将自己设计到一个角落时你可以做什么:召唤一条龙。
您还可以编写需要证明对 ConstrainedList
的元素没有约束的函数。这与 GHC 并不真正支持的 constraints
包和编程风格的领域相去甚远,但是没有足够的 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
}}
我们可以通过检查 Elem l a ~ ()
来检查 ConstrainedList
是否没有约束,但是如果它的约束以不同的方式编写,那将不起作用。
{-# 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
。约束包通过将它们具体化为 classes Class
and :=>
{-# 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'