的記錄更新語法
<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 @[email protected], update it, and then
-- return an interface back with @[email protected])
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}
來源
2013-07-20 15:36:03
JJJ
答案是「不」,但是「使用鏡頭」,但更重要的是,我覺得對這裏的某個地方存在什麼樣的類別存在根本的誤解。如果你說如果你想有這樣一個班,這將會有很大幫助;我們可能會提出一個更習慣的選擇。 –
@DanielWagner - 我添加了一個澄清,對我試圖解決這個問題 - 我希望它現在足夠多的清楚,爲什麼我試圖做到這一點:) –
看到我更新的答案。在第二個例子中,我使用'update'方法執行實際更新(可以使用實例中的記錄更新語法來實現),第三個示例使用'Control.Lens'。 – JJJ