Haskell中的容器类型类

时间:2019-06-04 08:10:13

标签: haskell containers

我有一个想法,创建一个Container类来普遍使用任何类似于容器的类型:

class Container c where
    at :: (Eq k) => c a -> k -> a

但是尝试将此类的实例用作(例如)List会令人失望:

{- not working code -}
instance Container [a] where
    at lst k = lst !! k

List要求Int,但我们只有Eq

• Couldn't match expected type ‘Int’ with actual type ‘k’
  ‘k’ is a rigid type variable bound by
    the type signature for:
      at :: forall k v. Eq k => [a] -> k -> v

我们不能这样:

class Container c where
    at :: c a -> Int -> v

因为整个点都丢失了,因为我们不仅希望将Int用作容器的“键”,还希望将其他Eq类型用作容器的“键”,例如String(Int, Int)Bool

以错误的方式进行操作的示例(我做到了,但我不喜欢它):

class Container c where
    at :: (Show k, Eq k) => c a -> k -> a


toInt :: (Show s) => s -> Int
toInt r = let s = show r
          in  (read s :: Int)


instance Container [] where
    at arr k = arr !! toInt k

{-
*R.Container> at [1,2,3] 1
2
-}

toBool :: (Show s) => s -> Bool
toBool r = let s = show r
           in  (read s :: Bool)


data MyPair a = MyPair { ifTrue :: a, ifFalse :: a } deriving (Show)


instance Container MyPair where
    at (MyPair a b) k = let yes = toBool k
                        in  if yes then a else b 

{-
*R.Container> let myPair = MyPair 12 13
*R.Container> at myPair True
12
*R.Container> at myPair False
13
-}

toIntPair :: (Show s) => s -> (Int,Int)
toIntPair r = let s = show r
           in  (read s :: (Int,Int))

data Matrix a = Matrix {arr2d :: [[a]]} deriving (Show)


instance Container Matrix where
    at mtrx k = let (f,s) = toIntPair k
                    arrs = arr2d mtrx
                    arr = arrs !! f
                in  arr !! s

{-
*R.Container> let mtrx = Matrix [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,12,11,10]] 
*R.Container> at mtrx (3,1)
12
*R.Container> at mtrx (3,0)
13
-}

因此,对于每个包含从Show到所需类型的缓慢转换的对话功能,我必须使用Eq约束

我的问题是如何在没有语言扩展({-# LANGUAGE)的情况下以最佳方式(良好实践)实施此想法?

1 个答案:

答案 0 :(得分:2)

看看Control.Lens.At,并在看到那里做了什么之后也许问一个更具体的问题?诚然,这是通过设置镜头的略有不同的目标完成的,但这实际上是相同的。我将尝试简要说明其工作原理。

首先,值得注意的是,最好将您想要的内容视为关联映射类型类而不是容器,因为您希望能够在k处查找值。

因此,Control.Lens.At为容器的键和值类型设置了两个类型族IndexIxValue。例如,如您所料,Map k v has Index (Map k v) = kIxValue (Map k v) = v(排除k必须为Ord),而列表可以被认为是从索引到其值thus Index [a] = IntIxValue [a] = a的映射。

这些类型族用于构造类型类Ixed,也就是说,给定容器:: ck = Index c之后,我可以检索到v :: Maybe (IxValue c)(取决于它是否存在) (或否)(如果需要,甚至可以在k处进行修改)。正如我之前说过的,这是为了设置镜头而完成的,但是如果需要,可以采用这种方法。


您还可以查看IsMap,了解设计空间中的另一点。那里也没有使用语言扩展。