2013-07-20 5 views
5

मैं कुछ डिफ़ॉल्ट विधियों के साथ Type Class को कार्यान्वित करना चाहता हूं, लेकिन मुझे एक त्रुटि मिल रही है, कि मैं type classes परिभाषाओं के अंदर record selectors का उपयोग नहीं कर सकता।हास्केल के प्रकार वर्गों में रिकॉर्ड चयनकर्ता

निम्नलिखित कोड मूल रूप से बनाता है type class जो add समारोह है, जो कुछ data type की 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} 

यह हास्केल में ऐसे तरीकों घोषित करने के लिए संभव है?

स्पष्टीकरण

मैं ऐसे डिजाइन की जरूरत है क्योंकि मैं कुछ data types, जो समान तरह से व्यवहार मिल गया है। आइए कहें, हमें A, B और Cdata types मिला। उनमें से प्रत्येक को repr :: DG.Gr a b रिकॉर्ड होना चाहिए, जहां a और bA, B और C प्रत्येक के लिए अलग हैं।

A, B और Cadd या delete (जो मूल रूप से जोड़ सकते हैं या repr रिकॉर्ड करने के लिए तत्वों हटाना) की तरह ही काम करता है, को साझा करें। यदि ये डेटा प्रकार बहुत सारे फ़ंक्शंस साझा करते हैं तो type class में फ़ंक्शंस को कार्यान्वित करने और type class के उदाहरण बनाने के लिए यह समझ में आता है - ये फ़ंक्शन स्वचालित रूप से हमारे प्रत्येक data type के लिए लागू किए जाएंगे।

अतिरिक्त मैं data types में से कुछ को प्यार करूंगा (मान लें कि मैं B चाहता हूं) add पर कॉल करते समय थोड़ा अलग व्यवहार करने के लिए। B के लिए type class के instance बनाते समय इस व्यवहार को कार्यान्वित करना आसान है।

+2

जवाब है "नहीं", लेकिन "लेंस का उपयोग करके,", लेकिन अधिक महत्वपूर्ण बात यह है कि मुझे लगता है कि यहां कहीं भी कक्षाएं क्या हैं, इसकी मूलभूत गलतफहमी है। यदि आपने कहा कि आप ऐसा वर्ग चाहते हैं तो यह बहुत मदद करेगा; हम एक और बेवकूफ विकल्प का सुझाव देने में सक्षम हो सकते हैं। –

+0

@DanielWagner - मैंने जिस समस्या को हल करने की कोशिश कर रहा हूं, उसके लिए मैंने एक स्पष्टीकरण जोड़ा है - मुझे आशा है कि अब यह स्पष्ट हो गया है, मैं ऐसा क्यों करने की कोशिश कर रहा हूं :) –

+0

मेरा अद्यतन उत्तर देखें। दूसरे उदाहरण में मैं 'अद्यतन' विधि का उपयोग करता हूं जो वास्तविक अद्यतन करता है (उदाहरणों में उस रिकॉर्ड अद्यतन वाक्यविन्यास का उपयोग करके कार्यान्वित किया जा सकता है), और तीसरा उदाहरण 'Control.Lens' का उपयोग करता है। – JJJ

उत्तर

3
  1. रिकॉर्ड अद्यतन वाक्य रचना

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

    काम करता है <record-instance> है जब एक जाना जाता बीजीय डेटा प्रकार का एक उदाहरण/अवधि (ताकि <record-field-name> है यह ज्ञात क्षेत्र), अपने कोड में यह सिर्फ है कुछ (ad-hoc) polymorphic पैरामीटर gr, इसलिए आपको पहले gr से Gr को परिवर्तित करने की आवश्यकता है, फिर इसे अपडेट करें, और उसके बाद ...

  2. मुझे लगता है कि 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} 
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 
संबंधित मुद्दे