我有一个想法,创建一个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
)的情况下以最佳方式(良好实践)实施此想法?
答案 0 :(得分:2)
看看Control.Lens.At,并在看到那里做了什么之后也许问一个更具体的问题?诚然,这是通过设置镜头的略有不同的目标完成的,但这实际上是相同的。我将尝试简要说明其工作原理。
首先,值得注意的是,最好将您想要的内容视为关联映射类型类而不是容器,因为您希望能够在k
处查找值。
因此,Control.Lens.At为容器的键和值类型设置了两个类型族Index
和IxValue
。例如,如您所料,Map k v
has Index (Map k v) = k
和IxValue (Map k v) = v
(排除k
必须为Ord
),而列表可以被认为是从索引到其值thus Index [a] = Int
,IxValue [a] = a
的映射。
这些类型族用于构造类型类Ixed
,也就是说,给定容器:: c
和k = Index c
之后,我可以检索到v :: Maybe (IxValue c)
(取决于它是否存在) (或否)(如果需要,甚至可以在k
处进行修改)。正如我之前说过的,这是为了设置镜头而完成的,但是如果需要,可以采用这种方法。
您还可以查看IsMap,了解设计空间中的另一点。那里也没有使用语言扩展。