我想使用几个默认方法实现Type Class
,但我收到错误,我无法在record selectors
定义中使用type classes
。
以下代码基本上创建了type class
,它定义了add
函数,它应该为某些repr
的{{1}}记录添加一个元素。这是代码:
data type
编译器抛出错误:
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}
是否可以在Haskell中声明此类方法?
澄清
我需要这样的设计,因为我有一些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}
,它以相同的方式表现。可以说,我们得到了data types
,A
和B
C
。他们每个人都应该有一个记录data types
,其中repr :: DG.Gr a b
和a
对于b
,A
和B
各有所不同。
C
,A
和B
共享相同的功能,例如C
或add
(基本上添加或删除要记录的元素{{1} })。如果这些数据类型共享许多函数,那么在delete
中实现函数并生成此repr
的实例是有意义的 - 这些函数将自动为每个type class
实现。< / p>
另外我希望其中一些type class
(假设我希望data type
)在调用data types
函数时表现得略有不同。在为B
add
instance
进行type class
时,很容易实现此行为。
答案 0 :(得分:3)
记录更新语法
<record-instance> { <record-field-name> = ..., ... }
当<record-instance>
是已知代数数据类型的实例/术语时(因为<record-field-name>
是已知字段),在您的代码中它只是一些( ad-hoc)多态参数gr
,因此您首先需要将gr
转换为Gr
,然后更新它,然后再...
我认为gr
和Gr
在某种意义上应该是等价的,即我们需要repr
的反函数,比如iface
,才能够实施add
。
以下是一个例子:
{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}
data Gr a b = Gr { _internal :: [(a, b)] } deriving ( Show, Read )
class Graph gr a b where
repr :: gr -> Gr a b
iface :: Gr a b -> gr
-- iface . repr == id {gr}
-- repr . iface == id {Gr a b}
-- add element via "interface" (get a representation via @repr@, update it, and then
-- return an interface back with @iface@)
add :: (a, b) -> gr -> gr
add el g = let r = repr g in iface r { _internal = el : _internal r }
-- or
add el = iface . insNode el . repr where
insNode x (Gr xs) = Gr (x : xs) -- or whatever
instance Graph String Int Int where
repr = read
iface = show
test :: String
test = add (1 :: Int, 2 :: Int) "Gr { _internal = [] }"
-- test => "Gr {_internal = [(1,2)]}"
如果某些数据类型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}
也可以在这里使用lenses:
{-# 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}
答案 1 :(得分:0)
您可以尝试这样的事情(使用元组列表作为示例,而不是DG
)
{-# 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