2013-07-20 2 views
5

Voglio implementare un Type Class con alcuni metodi predefiniti, ma sto ricevendo un errore, che non riesco a utilizzare record selectors all'interno delle definizioni type classes.Selettori di record nelle classi di tipi Haskell

Il codice seguente crea fondamentalmente type class che definisce add funzione, che deve aggiungere un elemento al repr record di qualche data type. Ecco il codice:

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} 

Il compilatore genera un errore:

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} 

E 'possibile dichiarare tali metodi a Haskell?

Chiarimento

ho bisogno di tale progetto perché ho avuto qualche data types, che si comportano in modo simmilar. Diciamo, abbiamo ottenuto A, B e Cdata types. Ciascuno di essi deve avere un record repr :: DG.Gr a b, dove a e sono distinti per ciascuno di A, B e C.

A, B e C condividono le stesse funzioni, come add o delete (che in pratica aggiungere o eliminare elementi per registrare repr). Se questi tipi di dati condividono molte funzioni, è opportuno implementare le funzioni in type class e creare istanze di questo type class - queste funzioni verranno implementate automaticamente per ciascuno dei nostri data type.

Ulteriori mi piacerebbe alcuni di questi data types (diciamo che voglio B) comportarsi leggermente diverso quando si chiama la funzione add su di esso. È facile implementare questo comportamento quando si effettua instance di type class per B.

+2

la risposta è "no", ma "più o meno, usando gli obiettivi", ma, cosa più importante, sento che c'è un malinteso fondamentale su quali classi ci siano da qualche parte qui. Sarebbe di grande aiuto se dicessi _why_ vuoi avere una tale classe; potremmo essere in grado di suggerire un'alternativa più idiomatica. –

+0

@DanielWagner - Ho aggiunto un chiarimento per il problema che sto cercando di risolvere - Spero che ora sia abbastanza chiaro, perché sto cercando di farlo :) –

+0

Vedere la mia risposta aggiornata. Nel secondo esempio, utilizzo il metodo 'update' che esegue l'aggiornamento effettivo (può essere implementato utilizzando la sintassi di aggiornamento del record nelle istanze) e il terzo esempio utilizza' Control.Lens'. – JJJ

risposta

3
  1. L'aggiornamento record di sintassi

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

    opere quando <record-instance> è un'istanza/termine di un noto tipo di dati algebrico (in modo che <record-field-name> è vero campo conosciuto), nel codice si tratta solo di alcuni (ad hoc) parametro polimorfico gr, quindi è necessario prima convertire gr in Gr, quindi aggiornarlo, quindi ...

  2. Penso che gr e Gr dovrebbero essere equivalenti in un certo senso, cioè abbiamo bisogno di una funzione inversa per repr, per esempio iface, per poter implementare add.

Ecco un esempio:

{-# 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)]}" 

Se alcuni tipi di dati A e BaggregataGr a b (in modo che non possiamo scrivere un inverso per repr), allora possiamo fare qualcosa del genere:

{-# 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} 

E 'anche possibile utilizzare lenses qui:

{-# 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

si può provare qualcosa di simile (che usa la lista tuple come ad esempio invece di 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