2012-04-18 38 views
14

Ví dụ: giả sử tôi muốn triển khai hàm tổng hợp danh sách Num s. Nửa chừng mã hóa nó, tôi muốn gỡ lỗi nó với Debug.Trace:Có thể ép buộc loại cho một lớp trong Haskell không?

module T where 
import Debug.Trace 

dosum :: (Num a) => [a] -> a 
dosum xs = dosum' 0 xs 
    where 
     dosum' n [] = n 
     dosum' n (x:xs) = trace (show n) $ dosum' (n+x) xs 

Vấn đề là điều này sẽ không biên dịch:

Could not deduce (Show a) arising from a use of dosum' 
from the context (Num a) 

tôi có thể thêm (Show a) để dosum và sau đó loại bỏ nó khi tôi đã hoàn thành gỡ lỗi (trong cuộc sống thực, tôi sẽ muốn có một loại mà không nhất thiết phải trong Show, nhưng tôi sẽ gỡ lỗi với số nguyên). Điều này có thể trở nên rườm rà nếu có một vài chức năng liên quan và tôi tiếp tục thêm việc xóa các câu lệnh Show a.

Tôi muốn có một hàm unsafeShow

unsafeShow :: a -> String 

mà hoạt động nếu aShow a và là miễn phí để sụp đổ nếu nó không phải là. Điều này có thể không?

+0

Bạn có thể nhận xét chữ ký trong khi gỡ lỗi. –

+0

@DanielFischer: vấn đề là nếu dosum là ở dưới cùng của một ngăn xếp, tôi kết thúc bình luận/uncommenting nhiều chức năng tất cả các thời gian và nó rất khó chịu. – luispedro

+1

Có lẽ (?) Ít xấu hơn: '#ifdef DEBUG' –

Trả lời

9

Câu trả lời thực sự khủng khiếp là sử dụng unsafeCoerce từ mô-đun Unsafe.Coerce. Nó là như nó âm thanh - nó là một công cụ chung cho bỏ qua hệ thống loại, và nếu bạn nhận được nó sai, bạn sẽ không nhận được một lỗi loại hoặc một ngoại lệ, bạn sẽ nhận được một lỗi phân đoạn.

Trong trường hợp này, bạn có thể unsafeCoerce giá trị mà bạn đã biết là Integer đến Integer để hệ thống kiểu có thể nhận ra rằng đó cũng là số nguyên. Sau đó, bạn có thể hiển thị nó như bình thường (đảm bảo cung cấp chữ ký rõ ràng, vì vậy show biết nội dung hiển thị - không thể suy ra, vì unsafeCoerce có thể trả về bất kỳ loại nào!)

Nhưng nếu bạn vô tình gọi mã với unsafeCoerce trên một cái gì đó khác hơn là một Integer, tai nạn, tham nhũng bộ nhớ, bất cứ điều gì có thể xảy ra - bạn đã hoàn toàn vứt bỏ mạng lưới an toàn của bạn.

Nhìn chung, chỉ sử dụng "an toàn" của unsafeCoerce là giữa các loại mà bạn đã biết đều bình đẳng, nhưng typechecker không (hoặc một số chuyên ngành trường hợp sử dụng khác, xem the docs). Thậm chí sau đó nó sẽ được rất nhiều cau mày bởi bất cứ ai đọc mã của bạn trừ khi bình luận của bạn giải thích tại sao nó là lựa chọn duy nhất.

+0

Cảm ơn! Điều này thực sự khá gần với những gì tôi muốn. Tôi không biết về '' unsafeCoerce'' – luispedro

+1

Tôi chấp nhận điều này như tôi đã viết 'showInt ai = show (unsafeCoerce ai :: Int) '' có kiểu '' showInt :: a -> String'' và thực hiện chính xác cái gì Tôi muốn. – luispedro

+0

Tôi thậm chí không tin rằng có bất kỳ cách nào để thực hiện công việc này giúp bạn ở bất cứ nơi nào mà 'show' bình thường không nhận được bạn. –

13

Không, điều này là không thể. Nó sẽ vi phạm parametricity; một chức năng đa hình không được phép hoạt động khác nhau dựa trên loại cụ thể mà nó được gọi với. Nó cũng sẽ phá vỡ giả định thế giới mở, trong đó thêm một thể hiện Show cho một loại sẽ thay đổi hành vi của chương trình của bạn. Đây có thể là một trợ giúp gỡ lỗi hữu ích, như một cái gì đó được đánh dấu rõ ràng không an toàn, nhưng GHC không hỗ trợ chức năng như vậy, và tôi không nghĩ rằng việc thực hiện hiện tại của nó sẽ cho phép bổ sung dễ dàng.

Một lựa chọn có thể, nếu bạn có nhiều chức năng với bối cảnh typeclass tương tự mà bạn muốn làm điều này với, và có một nhóm ngữ nghĩa khái niệm đến, sẽ có một lớp học như

class (Num a) => Number a 
instance (Num a) => Number a 

mà bạn có thể sử dụng thay vì Num trong chữ ký, thay đổi tuyên bố thành (Num a, Show a) khi gỡ lỗi. (Tuy nhiên, tốt hơn nên chọn tên có ý nghĩa hơn số Number!)

+0

Cảm ơn. Tôi đồng ý rằng đây là một ý tưởng khủng khiếp nói chung. Tôi chỉ muốn nó để gỡ lỗi. – luispedro

+1

Trong GHC 7.4.1 với 'ConstraintKinds' bạn cũng có thể viết' type Number a = Num a' hoặc 'type Number a = (Hiển thị a, Num a)' và sau đó viết hàm 'f :: (Number a) = > a -> a -> a' –

+0

Giải thích hay. Tôi biết rằng điều đó là không thể, nhưng câu này đã xóa nó cho tôi: "thêm một thể hiện cho một loại sẽ thay đổi hành vi của chương trình của bạn." –

3

Điều này là không thể. (Lưu ý 1)


1: Một ngoại lệ là bạn có thể đổ cấu trúc đống đống GHC của, thông qua một chức năng a -> String. Bạn có thể ví dụ: luôn chuyển giá trị thành giá trị con trỏ thập lục phân, qua vacuum. Đây không phải là điều bạn muốn. Chức năng này giống với chức năng được trình gỡ lỗi GHCi sử dụng để hiển thị các giá trị heap tùy ý.

3

Không thể triển khai chức năng unsafeShow của bạn trong Haskell thuần túy. GHC có thể cung cấp một, nhưng hiện tại thì không.

Tuy nhiên, bạn có thể xem trình gỡ rối GHCi. Điều này cho phép bạn in ra những thứ không có ví dụ Show. (Hơn nữa, nó cho phép bạn tránh đánh giá thứ gì đó không được đánh giá khác, điều này có thể hữu ích.)

0

Đó là phiên bản GHC 7.4.1?Từ số này release notes:

Lớp Num không còn có Eq hoặc Hiển thị siêu lớp. Do đó, một số lớp và hàm khác đã thu được các ràng buộc Eq hoặc Show rõ ràng hơn là dựa vào ràng buộc Num để cung cấp chúng.

Bạn có thể làm cho mã làm việc với cả hai Haskell98/Haskell2010 và GHC bởi:

  • Bất cứ khi nào bạn thực hiện một trường hợp Num của một loại, cũng làm cho Show và Eq trường hợp, và

  • Bất cứ khi nào bạn đưa ra một hàm, instance hoặc class một ràng buộc Num t, cũng cho nó ràng buộc Show t và Eq t.

Mã của bạn hoạt động tốt trong các phiên bản trước của GHC (Tôi thử trong 7.0.4).

+0

Vâng, đó là GHC hiện đại. '' Num'' là một ví dụ đã hoạt động tốt trên GHC hiện đại, nhưng tôi muốn có câu hỏi tổng quát hơn (ví dụ thực tế của tôi là ByteStrings, nhưng tôi nghĩ số nguyên sẽ tạo ra một ví dụ đơn giản hơn). – luispedro

+0

Ok, tôi hiểu, nếu 'foo' có' Hiển thị' trong ngữ cảnh của nó và 'bar' gọi' foo' thì 'bar' phải có' Hiển thị' trong ngữ cảnh của nó, v.v. Có lẽ bạn cần phải reimplement cũ 'Num' như thế này' lớp (Hiển thị t, Eq t, Num t) => NumDebug t' và sau đó chuyển đổi giữa 'Num' và' NumDebug'? Nếu bạn có 'dấu vết (hiển thị n) ...' khi 'n' là phi đa hình phi-hoc (nói,' Số nguyên ') thì không có vấn đề gì. – JJJ

1

Thông thường tôi sẽ bình luận ra chữ ký loại, nhưng nếu một hàm nằm sâu trong cây gây khó chịu. Bạn có thể thử sử dụng các quy tắc viết lại để thay thế chức năng đa hình của bạn bằng biến thể được sửa đổi.

-- original function, should add NOINLINE to make sure your rule gets a chance to fire. 
{-# NOINLINE dosum #-} 
dosum :: (Num a) => [a] -> a 

-- a version with added debugging 
dosumShow :: (Num a, Show a) => [a] -> a 

{-# RULES "sub/dosum" forall x. dosum x = dosumShow x #-} 
Các vấn đề liên quan