Tại sao tôi không thể đặt String thành một phiên bản của typeclass?


85

Đã cho :

data Foo =
  FooString String
class Fooable a where --(is this a good way to name this?)
  toFoo :: a -> Foo

Tôi muốn tạo Stringmột ví dụ về Fooable:

instance Fooable String where
  toFoo = FooString

GHC sau đó phàn nàn:

Illegal instance declaration for `Fooable String'
    (All instance types must be of the form (T t1 ... tn)
     where T is not a synonym.
     Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Fooable String'

Nếu thay vào đó tôi sử dụng [Char]:

instance Fooable [Char] where
  toFoo = FooString

GHC phàn nàn:

Illegal instance declaration for `Fooable [Char]'
   (All instance types must be of the form (T a1 ... an)
    where a1 ... an are type *variables*,
    and each type variable appears at most once in the instance head.
    Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Fooable [Char]'

Câu hỏi :

  • Tại sao tôi không thể tạo Chuỗi và phiên bản của một typeclass?
  • GHC có vẻ sẵn sàng để tôi giải quyết vấn đề này nếu tôi thêm một lá cờ bổ sung. Đây có phải là một ý tưởng tốt?

6
Đây là loại câu hỏi tôi ủng hộ và đánh dấu là yêu thích bởi vì nếu không, tôi biết trong tương lai gần tôi sẽ hỏi nó;)
Oscar Mederos

3
Về cờ phụ: có lẽ là một ý kiến ​​hay, miễn là bạn tin tưởng GHC và hiểu cờ làm gì. Yesod nghĩ đến: nó khuyến khích bạn luôn sử dụng pragma OverloadedStrings khi viết ứng dụng Yesod và QuasiQuotes là một điều cần thiết cho các quy tắc định tuyến Yesod. Lưu ý rằng thay vì cờ tại thời điểm biên dịch, bạn cũng có thể đặt {-# LANGUAGE FlexibleInstances #-}(hoặc bất kỳ pragma nào khác) ở đầu tệp .hs của mình.
Dan Burton

Câu trả lời:


65

Điều này là do Stringchỉ là một bí danh kiểu cho [Char], chỉ là ứng dụng của phương thức tạo kiểu []trên kiểu Char, vì vậy đây sẽ là dạng ([] Char). mà không có dạng (T a1 .. an)Charkhông phải là một biến kiểu.

Lý do cho sự hạn chế này là để ngăn chặn các trường hợp chồng chéo. Ví dụ: giả sử bạn có một instance Fooable [Char], và sau đó một người nào đó đã đến và xác định một instance Fooable [a]. Bây giờ trình biên dịch sẽ không thể tìm ra cái nào bạn muốn sử dụng và sẽ đưa ra lỗi cho bạn.

Bằng cách sử dụng -XFlexibleInstances, về cơ bản bạn đã hứa với trình biên dịch rằng bạn sẽ không xác định bất kỳ trường hợp nào như vậy.

Tùy thuộc vào những gì bạn đang cố gắng hoàn thành, có thể tốt hơn là xác định một trình bao bọc:

newtype Wrapper = Wrapper String
instance Fooable Wrapper where
    ...

4
Hãy nói để tranh luận rằng tôi cũng thực sự muốn instance Fooable [a]. Có cách nào để làm cho toFoohàm hoạt động khác nếu alà Char không?
John F. Miller

7
@John: Có một tiện ích mở rộng -XOverlappingInstancessẽ cho phép điều này và chọn trường hợp cụ thể nhất. Xem hướng dẫn sử dụng GHC để biết thêm chi tiết .
hammar,

18

Bạn đang gặp phải hai hạn chế của kính chữ Haskell98 cổ điển:

  • họ không cho phép nhập từ đồng nghĩa trong các trường hợp
  • chúng không cho phép các kiểu lồng nhau không chứa các biến kiểu.

Những hạn chế khó chịu này được dỡ bỏ bởi hai phần mở rộng ngôn ngữ:

  • -XTypeSynonymInstances

cho phép bạn sử dụng loại từ đồng nghĩa (như Stringfor [Char]) và:

  • -XFlexibleInstances

giúp loại bỏ các hạn chế đối với các kiểu đối tượng có dạng T a b ..trong đó các tham số là biến kiểu. Các -XFlexibleInstanceslá cờ cho phép người đứng đầu tuyên bố dụ đề cập đến các loại lồng nhau tùy ý.

Lưu ý rằng việc dỡ bỏ các hạn chế này đôi khi có thể dẫn đến các trường hợp chồng chéo , tại thời điểm đó, có thể cần một phần mở rộng ngôn ngữ bổ sung để giải quyết sự không rõ ràng, cho phép GHC chọn một trường hợp cho bạn.


Tài liệu tham khảo ::


4

Linh hoạt không phải là một câu trả lời tốt trong hầu hết các trường hợp. Các lựa chọn thay thế tốt hơn là gói Chuỗi trong một kiểu mới hoặc giới thiệu một lớp trợ giúp như vậy:

class Element a where
   listToFoo :: [a] -> Foo

instance Element Char where
   listToFoo = FooString

instance Element a => Fooable [a] where
   toFoo = listToFoo

Xem thêm: http://www.haskell.org/haskellwiki/List_instance


2

Thêm vào những câu trả lời này, nếu bạn không thoải mái với việc dỡ bỏ các hạn chế, có thể có những trường hợp có thể hợp lý khi quấn Chuỗi của bạn trong một kiểu mới, có thể là một phiên bản của một lớp. Sự đánh đổi sẽ là sự xấu xí tiềm ẩn, phải quấn và mở mã của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.