2013-07-20 33 views
5

Tôi muốn triển khai Type Class với một vài phương pháp mặc định, nhưng tôi gặp lỗi, rằng tôi không thể sử dụng record selectors bên trong các định nghĩa type classes.Bộ chọn bản ghi trong các Loại Lớp của Haskell

Mã sau về cơ bản tạo type class xác định hàm add, sẽ thêm phần tử vào bản ghi repr của một số data type. Đây là mã:

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} 

Trình biên dịch ném lỗi:

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} 

Có thể khai báo các phương pháp như vậy trong Haskell?

Làm rõ

Tôi cần thiết kế như vậy bởi vì tôi đã có một số data types, mà cư xử theo cách simmilar. Giả sử, chúng tôi có A, BCdata types. Mỗi người trong số họ phải có hồ sơ repr :: DG.Gr a b, trong đó ab riêng biệt cho mỗi A, BC.

A, BC chia sẻ các chức năng tương tự, như add hoặc delete (mà về cơ bản thêm hoặc xóa các yếu tố để ghi repr). Nếu các kiểu dữ liệu này chia sẻ rất nhiều hàm, hãy thực hiện các hàm trong type class và thực hiện các trường hợp này type class - các chức năng này sẽ được triển khai cho mỗi data type tự động của chúng tôi.

Bổ sung Tôi rất thích một số trong số này data types (cho phép nói rằng tôi muốn B) để hoạt động nhẹ khác nhau khi gọi chức năng add trên đó. Thật dễ dàng để thực hiện hành vi này khi thực hiện instance của type class cho B.

+2

Câu trả lời là "không", nhưng "loại, sử dụng ống kính", nhưng quan trọng hơn là tôi cảm thấy có một sự hiểu lầm cơ bản về những lớp học ở đâu đó ở đây. Nó sẽ giúp ích rất nhiều nếu bạn nói _why_ bạn muốn có một lớp học như vậy; chúng tôi có thể đề xuất một giải pháp thay thế thành ngữ hơn. –

+0

@DanielWagner - Tôi đã thêm một giải thích cho vấn đề tôi đang cố gắng giải quyết - Tôi hy vọng nó là đủ rõ ràng bây giờ, tại sao tôi đang cố gắng để làm điều này :) –

+0

Xem câu trả lời cập nhật của tôi. Trong ví dụ thứ hai, tôi sử dụng phương thức 'update' để thực hiện cập nhật thực tế (có thể được thực hiện bằng cách sử dụng cú pháp cập nhật bản ghi đó trong các cá thể), và ví dụ thứ ba sử dụng' Control.Lens'. – JJJ

Trả lời

3
  1. Bản cập nhật kỷ lục cú pháp

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

    công trình khi <record-instance> là một ví dụ/hạn của một biết kiểu dữ liệu đại số (để <record-field-name> là nó lĩnh vực được biết đến), trong mã của bạn nó chỉ là một số thông số đa hình (ad-hoc) gr, vì vậy trước tiên bạn cần chuyển đổi gr thành Gr, sau đó cập nhật ...

  2. Tôi nghĩ rằng grGr phải tương đương theo một nghĩa nào đó, nghĩa là chúng tôi cần một hàm nghịch đảo cho repr, giả sử iface, để có thể triển khai add.

Dưới đây là một ví dụ:

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

Nếu một số kiểu dữ liệu ABtổngGr a b (vì vậy mà chúng ta không thể viết một nghịch đảo cho repr), sau đó chúng ta có thể làm điều gì đó như sau:

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

Nó cũng có thể sử dụng lenses đây:

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

Bạn có thể thử một cái gì đó như thế này (trong đó sử dụng danh sách tuple làm ví dụ thay vì 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 
Các vấn đề liên quan