2013-07-20 38 views
5

我想實現一個Type Class與幾個默認方法,但我收到一個錯誤,我不能使用record selectorstype classes定義。Haskell的類型記錄選擇器

以下代碼基本上創建type class限定add功能,它應該將元素添加到的一些data typerepr記錄。下面是代碼:

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這樣的方法?

澄清

我需要這樣的設計,因爲我已經有了一些data types,這表現在simmilar方式。可以說,我們得到了ABCdata types。他們中的每一個應該具有記錄repr :: DG.Gr a b,其中ab對於ABC中的每一個都是不同的。

ABC共享相同的功能,如adddelete(其基本上添加或刪除元素來記錄repr)。如果這些數據類型分享了很多的功能是有意義的執行功能type class,使這個type class的實例 - 這些功能將我們每個自動data type來實現。

其他我會喜歡其中一些data types(可以說我想B)在調用add函數時表現略有不同。當爲B製作type classinstance時,很容易實現此行爲。

+2

答案是「不」,但是「使用鏡頭」,但更重要的是,我覺得對這裏的某個地方存在什麼樣的類別存在根本的誤解。如果你說如果你想有這樣一個班,這將會有很大幫助;我們可能會提出一個更習慣的選擇。 –

+0

@DanielWagner - 我添加了一個澄清,對我試圖解決這個問題 - 我希望它現在足夠多的清楚,爲什麼我試圖做到這一點:) –

+0

看到我更新的答案。在第二個例子中,我使用'update'方法執行實際更新(可以使用實例中的記錄更新語法來實現),第三個示例使用'Control.Lens'。 – JJJ

回答

3
  1. 的記錄更新語法

    <record-instance> { <record-field-name> = ..., ... } 
    

    作品時<record-instance>代數數據類型的實例/學期(這樣<record-field-name>被它稱爲字段),在你的代碼,這只是一些(ad-hoc)多態參數gr,因此您需要先將gr轉換爲Gr,然後進行更新,然後...

  2. 我認爲grGr應該在某種意義上是等價的,即我們需要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)]}" 

如果某些數據類型AB骨料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} 
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