2012-03-11 16 views
7

Giả sử tôi có một kiểu dữ liệu nhưhaskell - bất kỳ cách nào để tạo ra các trường hợp "phát sinh" cho các kiểu dữ liệu đẳng thức-đẳng cấu?

data D a = D a a a 

và typeclass

class C c ... 
instance (C c1, C c2) => C (c1, c2) 

Sau đó, tôi muốn để có thể viết

data D a = D a a a deriving C 

và có tạo ra một thể hiện,

instance C ((a, a), a) => C (D a) 

bằng đẳng cấu modulo-lười biếng đánh giá,

D a ~ ((a, a), a) 

Note. Sử dụng newtype và GeneralizedNewtypeDeriving sẽ không hoạt động nếu, ví dụ: one có data D m = D (m Integer) (m Integer).

Lưu ý 2. Câu hỏi này có liên quan đến tính biểu cảm của Haskell nói chung - các ngôn ngữ như Python có cái gọi là tuple có tên, có thể được sử dụng ở bất cứ nơi nào các bộ dữ liệu được sử dụng; câu hỏi này cho thấy nơi/làm thế nào tôi không biết làm thế nào để thi đua cùng một điều trong Haskell.

Trả lời

14

Bạn có thể thực hiện việc này tương đối rõ ràng và hiệu quả bằng cách sử dụng generic programming support của GHC 7.4. documentation for GHC.Generics có thể hữu ích. Đây là một ví dụ.

Hãy xem xét ví dụ sau lớp và một số trường hợp mẫu:

class C a where 
    -- | Double all numbers 
    double :: a -> a 

instance C Int where 
    double i = 2 * i 

instance (C a, C b) => C (a, b) where 
    double (a, b) = (double a, double b) 

Chúng tôi cần một số pragmas ngôn ngữ và nhập khẩu:

{-# LANGUAGE TypeOperators, DefaultSignatures, DeriveGeneric, FlexibleContexts, FlexibleInstances #-} 
module Example where 

import GHC.Generics hiding(C, D) 

Bây giờ chúng ta đưa ra một số "trường hợp chung chung". Các loại generic tất cả đều có một tham số phantom x, mà làm cho dụ đứng đầu phức tạp hơn một chút:

-- "Insert" a normal value into a generic value 
instance C c => C (K1 i c x) where 
    double (K1 c) = K1 (double c) 

-- Ignore meta-information (constructor names, type names, field names) 
instance C (f x) => C (M1 i c f x) where 
    double (M1 f) = M1 (double f) 

-- Tuple-like instance 
instance (C (f x), C (g x)) => C ((f :*: g) x) where 
    double (f :*: g) = double f :*: double g 

Bây giờ chúng ta xác định lại đẳng cấp của chúng tôi C để tận dụng lợi thế của GC

class C a where 
    -- | Double all numbers 
    double :: a -> a 

    -- specify the default implementation for double 
    default double :: (Generic a, C (Rep a())) => a -> a 
    double = to0 . double . from0 

-- from, with a more specialised type, to avoid ambiguity 
from0 :: Generic a => a -> Rep a() 
from0 = from 

-- to, with a more specialised type, to avoid ambiguity 
to0 :: Generic a => Rep a() -> a 
to0 = to 

Bây giờ chúng ta có thể xác định một số các trường hợp rất dễ dàng:

data D a = D a a a deriving Generic 
instance C a => C (D a) 

data D2 m = D2 (m Int) (m Int) deriving Generic 
instance C (D2 D) 
+3

Cập nhật câu trả lời của tôi để tránh lớp trợ giúp; xem http://article.gmane.org/gmane.comp.lang.haskell.cafe/97079 – reinerp

Các vấn đề liên quan