2014-06-30 14 views
6

Giả sử tôi có một kiểu dữ liệu như thế này:có điều kiện lấy Hiện cho loại hiện sinh tham số vào loại constructor

{-# LANGUAGE RankNTypes #-} 
data X a = forall b. Show b => X (a b) 

Tôi muốn lấy được Show (X a), nhưng tất nhiên tôi chỉ có thể làm như vậy nếu có một thể hiện của Show (a b). Tôi đang bị cám dỗ để viết

{-# LANGUAGE StandaloneDeriving #-} 
deriving instance Show (a b) => Show (X a) 

nhưng tiếc là kiểu biến b không có sẵn trong bối cảnh dụ bởi vì nó được ràng buộc bởi các forall.

nỗ lực tiếp theo của tôi là để di chuyển Show (a b) bối cảnh vào forall trong định nghĩa kiểu dữ liệu, như vậy:

data X a = forall b. Show (a b) => X (a b) 
deriving instance Show (X a) 

này biên dịch, nhưng tiếc là bây giờ tôi đã mất khả năng để xây dựng một X với một unshowable (a b).

Có cách nào để cho phép X được xây dựng với bất kỳ (a b) nào và sau đó có điều kiện lấy được Show (X a) chỉ khi (a b) có thể hiển thị không?

Trả lời

7

Đây là sự thiếu hụt trong các lớp học Prelude. Có một cách tốt đẹp xung quanh nó mặc dù thể hiện trong gói prelude-extras. Tôi sẽ phác thảo nó bên dưới.

Chúng tôi muốn tạo lớp học cao cấp hơn Show. Nó trông như thế này

class Show1 a where 
    show1 :: Show b => a b -> String 

Sau đó, chúng ta có thể bày tỏ ít nhất một cách chính xác hạn chế mong muốn của chúng tôi như

deriving instance Show1 a => Show (X a) 

Thật không may, trình biên dịch vẫn chưa có đủ thông tin để đạt được nguồn gốc này. Chúng tôi cần phải chứng minh rằng (Show b, Show1 a) là đủ để lấy được Show (a b). Để làm như vậy chúng tôi sẽ cần phải kích hoạt một số (đáng sợ, nhưng sanely-sử dụng) mở rộng

{-# LANGUAGE FlexibleInstances   #-} 
{-# LANGUAGE OverlappingInstances  #-} 

instance (Show b, Show1 a) => Show (a b) where 
    show = show1 

Và bây giờ chúng ta có bằng chứng rằng trình biên dịch sẽ có thể lấy được những gì chúng ta cần

data X a = forall b . Show b => X (a b) 
deriving instance Show1 a => Show (X a) 
+0

Quá tệ. Mọi thứ mà sổ tay GHC nói về OverlappingInstances đều đáng sợ. – Will

+0

Vâng, nó không được khuyến khích tuyệt vời. Show1 cũng có thể clobber các trường hợp khác khá dễ dàng. –

5

Tôi sẽ có cách tiếp cận tương tự nhưng hơi khác với câu trả lời của J. Abrahamson.

chính xác những gì bạn yêu cầu không thể được thực hiện, bởi vì các lớp học kiểu cần phải được giải quyết tĩnh, nhưng sự tồn tại của Show (a b) có thể động tùy thuộc vào instantation của b. Sự khởi tạo này được ẩn bên trong giá trị X và do đó không hiển thị với trình kiểm tra loại khi bạn không có gì ngoài một nguồn gốc không xác định là X b.

Nó sẽ được tốt đẹp để viết một điều kiện trên a như Show (a b) tồn tại bất cứ khi nào Show b không, bởi vì khi đó sự tồn tại của Show (a b) không phải là thực sự phụ thuộc vào b như chúng ta đã biết rằng Show b luôn luôn là sự thật.

Chúng tôi không thể viết điều kiện trực tiếp, nhưng chúng ta có thể thể hiện một cái gì đó giống như nó sử dụng GADTs:

{-# LANGUAGE GADTs #-} 
data ShowDict a where 
    ShowDict :: Show a => ShowDict a 

Loại ShowDict a cung cấp một loại reification của lớp Show a - đó là một cái gì đó chúng ta có thể vượt qua xung quanh và định nghĩa các hàm.

Đặc biệt chúng tôi bây giờ có thể định nghĩa một lớp Show1 thể hiện điều kiện Show (a b) bất cứ khi nào chúng tôi có một Show b:

class Show1 a where 
    show1Dict :: ShowDict b -> ShowDict (a b) 

Và bây giờ chúng ta có thể xác định Show (X a) về Show1, bằng cách xây dựng ShowDict (a b) và sau đó mô hình khớp trên đó để tiết lộ cá thể Show:

{-# LANGUAGE ScopedTypeVariables #-} 
instance Show1 a => Show (X a) where 
    show (X (v :: a b)) = 
     case show1Dict ShowDict :: ShowDict (a b) of 
      ShowDict -> "X (" ++ show v ++ ")" 

Thực hiện đầy đủ hơn cũng sẽ có lude các thành viên khác của Show (showsPrecshowList).

Những điều tốt đẹp về giải pháp này là chúng ta có thể dễ dàng xác định Show1 cho [], tự động tái sử dụng cơ bản Show dụ:

instance Show1 [] where 
    show1Dict ShowDict = ShowDict 

Tôi cũng muốn tránh sự rất chung chung Show (a b) dụ từ câu trả lời J. Abrahamson của, nhưng nhược điểm của việc đặt logic trong ví dụ Show cho X là chúng ta sẽ phải thực hiện nó theo cách thủ công thay vì nhận được hành vi tự động dẫn xuất cho hàm tạo.

+0

Tôi đồng ý với câu trả lời này nói chung, nhưng đáng chú ý là các giải pháp này mất đi sự phát sinh. Đó có thể chỉ đơn giản là một hậu quả không thể tránh khỏi, mặc dù. Phát sinh và tồn tại không chơi độc đáo rất thường xuyên. –

+0

Tôi thực sự đã quên mất việc hiển thị hàm tạo 'X' trong cá thể' Show', đây là một câu trả lời đáng kể cho câu trả lời của bạn. Nhưng có, nó sẽ không thường xuyên có thể sử dụng bắt nguồn từ một tồn tại. –

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