在Haskell'中记录选择器;s型类
我想用很少的默认方法实现一个在Haskell'中记录选择器;s型类,haskell,types,record,typeclass,Haskell,Types,Record,Typeclass,我想用很少的默认方法实现一个类型类,但是我得到了一个错误,我不能在类型类定义中使用记录选择器 下面的代码基本上创建了type类,该类定义了add函数,该函数应该向一些数据类型的repr记录中添加一个元素。代码如下: import qualified Data.Graph.Inductive as DG class Graph gr a b where empty :: DG.Gr a b empty = DG.empty repr :: gr -> DG
类型类
,但是我得到了一个错误,我不能在类型类
定义中使用记录选择器
下面的代码基本上创建了type类
,该类定义了add
函数,该函数应该向一些数据类型
的repr
记录中添加一个元素。代码如下:
import qualified Data.Graph.Inductive as DG
class Graph gr a b where
empty :: DG.Gr a b
empty = DG.empty
repr :: gr -> DG.Gr a b
-- following function declaration does NOT work:
add :: a -> gr -> gr
add el g = g{repr = DG.insNode el $ repr g}
编译器抛出错误:
repr is not a record selector
In the expression: g {repr = DG.insNode el $ repr g}
In an equation for add:
add el g = g {repr = DG.insNode el $ repr g}
可以在Haskell中声明这样的方法吗
澄清
我需要这样的设计,因为我有一些数据类型
,它们的行为类似。比如说,我们得到了A
、B
和C
数据类型。它们中的每一个都应该有一个记录repr::DG.Gr a b
,其中a
和b
对于a
、b
和C
都是不同的
A
、B
和C
共享相同的功能,如add
或delete
(基本上添加或删除元素以记录repr
)。如果这些数据类型共享许多函数,那么实现类型类中的函数并创建该类型类的实例是有意义的-这些函数将自动为每个数据类型实现
另外,在调用add
函数时,我希望其中一些数据类型
(假设我希望B
)的行为稍有不同。当为B
创建类型类的实例时,很容易实现此行为
记录更新语法
<record-instance> { <record-field-name> = ..., ... }
如果某些数据类型A
和B
聚合Gr A B
(因此我们无法为repr
编写反向),那么我们可以这样做:
{-# LANGUAGE MultiParamTypeClasses #-}
data Gr a b = Gr [(a, b)] deriving ( Show )
class Graph gr a b where
repr :: gr -> Gr a b
update :: gr -> (Gr a b -> Gr a b) -> gr
-- 2: update :: gr -> Gr a b -> gr
add :: (a, b) -> gr -> gr
add el g = update g $ insNode el
-- 2: update g (insNode el $ repr g)
where insNode x (Gr xs) = Gr (x : xs)
data A = A { _aRepr :: Gr Char Char, _aRest :: Char } deriving ( Show )
data B = B { _bRepr :: Gr Int Int, _bRest :: Int } deriving ( Show )
instance Graph A Char Char where
repr = _aRepr
update r f = r { _aRepr = f $ _aRepr r }
-- 2: update r g = r { _aRepr = g }
instance Graph B Int Int where
repr = _bRepr
update r f = r { _bRepr = f $ _bRepr r }
-- 2: update r g = r { _bRepr = g }
testA :: A
testA = add ('1', '2') $ A (Gr []) '0'
-- => A {_aRepr = Gr [('1','2')], _aRest = '0'}
testB :: B
testB = add (1 :: Int, 2 :: Int) $ B (Gr []) 0
-- => B {_bRepr = Gr [(1,2)], _bRest = 0}
也可以在此处使用:
您可以尝试类似的方法(使用元组列表作为示例,而不是DG
)
答案是“不”,但“某种程度上,使用镜头”,但更重要的是,我觉得人们对这里的课程有一个根本性的误解。如果你说你为什么想上这样一门课,那会很有帮助;我们或许可以提出一个更为惯用的替代方案。@DanielWagner-我已经为我试图解决的问题添加了一个说明-我希望现在还不太清楚,我为什么要这样做:)请参阅我的最新答案。在第二个示例中,我使用执行实际更新的update
方法(可以在实例中使用记录更新语法实现),第三个示例使用Control.Lens
@JJJ,我想Lens就是我要找的!非常感谢。
{-# LANGUAGE MultiParamTypeClasses #-}
data Gr a b = Gr [(a, b)] deriving ( Show )
class Graph gr a b where
repr :: gr -> Gr a b
update :: gr -> (Gr a b -> Gr a b) -> gr
-- 2: update :: gr -> Gr a b -> gr
add :: (a, b) -> gr -> gr
add el g = update g $ insNode el
-- 2: update g (insNode el $ repr g)
where insNode x (Gr xs) = Gr (x : xs)
data A = A { _aRepr :: Gr Char Char, _aRest :: Char } deriving ( Show )
data B = B { _bRepr :: Gr Int Int, _bRest :: Int } deriving ( Show )
instance Graph A Char Char where
repr = _aRepr
update r f = r { _aRepr = f $ _aRepr r }
-- 2: update r g = r { _aRepr = g }
instance Graph B Int Int where
repr = _bRepr
update r f = r { _bRepr = f $ _bRepr r }
-- 2: update r g = r { _bRepr = g }
testA :: A
testA = add ('1', '2') $ A (Gr []) '0'
-- => A {_aRepr = Gr [('1','2')], _aRest = '0'}
testB :: B
testB = add (1 :: Int, 2 :: Int) $ B (Gr []) 0
-- => B {_bRepr = Gr [(1,2)], _bRest = 0}
{-# LANGUAGE MultiParamTypeClasses, TemplateHaskell #-}
import Control.Lens
data Gr a b = Gr [(a, b)] deriving ( Show )
insNode :: (a, b) -> Gr a b -> Gr a b
insNode x (Gr xs) = Gr (x : xs)
class Graph gr a b where
reprLens :: Simple Lens gr (Gr a b)
add :: Graph gr a b => (a, b) -> gr -> gr
add el = reprLens %~ insNode el
data A = A { _aRepr :: Gr Char Char, _aRest :: Char } deriving ( Show )
data B = B { _bRepr :: Gr Int Int, _bRest :: Int } deriving ( Show )
makeLenses ''A
makeLenses ''B
instance Graph A Char Char where
reprLens = aRepr
instance Graph B Int Int where
reprLens = bRepr
main :: IO ()
main = do
let a = A (Gr []) '0'
b = B (Gr []) 0
print $ add ('0', '1') a
print $ add (0 :: Int, 1 :: Int) b
-- A {_aRepr = Gr [('0','1')], _aRest = '0'}
-- B {_bRepr = Gr [(0,1)], _bRest = 0}
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, TypeSynonymInstances #-}
class MyClass g a b | g -> a b where
extract :: g -> [(a,b)]
construct :: [(a,b)] -> g
empty :: g
empty = construct []
add :: (a,b) -> g -> g
add i d = construct $ [i] ++ (extract d)
data A = A {reprA :: [(Int,Int)]}
instance MyClass A Int Int where
extract = reprA
construct = A
data B = B {reprB :: [(String,String)]}
instance MyClass B String String where
extract = reprB
construct = B