Haskell - Ví dụ Monad tự động


8

Tôi đang cố gắng tạo kiểu dữ liệu của riêng mình, đây sẽ là một phần của lớp Monad, nhưng

newtype Container a = Container a deriving Monad

cho tôi lỗi này:

   * Can't make a derived instance of `Monad Container'
        (even with cunning GeneralizedNewtypeDeriving):
        cannot eta-reduce the representation type enough
    * In the newtype declaration for `Container'
   |
30 | newtype Container a = Container a deriving Monad

Nó hoạt động tốt đối với các lớp khác (Ví dụ: Hiển thị), nhưng không phải cho Monad, vậy làm cách nào tôi có thể thuyết phục ghci để thể hiện lớp Container của mình sang lớp Monad?

Cảm ơn


1
Vấn đề là đó akhông phải là một ví dụ của đơn nguyên, do đó nó không có nhiều ý nghĩa. Nếu bạn ví dụ sẽ sử dụng newtype Container a = Container [a] deriving (Functor, Applicative, Monad)nó sẽ hoạt động, vì []là một ví dụ của Monad.
Willem Van Onsem

2
GenerlizedNewtypeDerivingđặc biệt để "nâng" các thể hiện của kiểu được bọc sang loại mới. Câu hỏi về cách (hoặc nếu) người ta có thể tự động lấy ra một Monadví dụ cho Containervẫn còn thú vị. (Thực tế basexác định Monadví dụ Identityrõ ràng cho thấy bạn không thể.)
chepner

Monadkhông phải là một trong những kiểu chữ mà tiêu chuẩn Haskell có sẵn để tự động xuất phát ( Showcùng với một số kiểu cơ bản khác). GHC có thể làm điều đó với (các) phần mở rộng chính xác, tôi tin.
Robin Zigmond

@RobinZigmond Lưu ý rằng thông báo cho biết đã GeneralizedNewtypeDerivingđược bật và một câu hỏi là tại sao nó vẫn không hoạt động.
Alexey Romanov

Câu trả lời:


9

Nó hoạt động tốt cho các lớp khác (Ví dụ hiển thị)

Chỉ có một tập hợp các lớp tiêu chuẩn cố định hỗ trợ xuất phát từ hộp:

Trong Haskell 98, các lớp phái sinh duy nhất là Eq, Ord, Enum, Ix, Bounded, Read và Show. Các phần mở rộng ngôn ngữ khác nhau mở rộng danh sách này.

--- Hướng dẫn sử dụng GHC

Đặc biệt Monadkhông thuộc danh sách đó, cũng không thuộc danh sách mở rộng.

Có nhiều phần mở rộng khái quát hóa việc tạo ra các lớp tùy ý, nhưng chúng không thể tự động 100%. Ai đó ở đâu đó phải xác định cách thức thực hiện đó; tùy thuộc vào lớp học, người dùng có thể được yêu cầu mang gánh nặng vì có thông tin về cơ bản không thể suy ra.

Trong trường hợp của bạn, newtype Containertương đương với Identityđơn nguyên trong thư viện chuẩn, vì vậy bạn có thể sử dụng DerivingVia:

{-# LANGUAGE DerivingVia #-}
import Data.Functor.Identity

newtype Container a = Container a deriving (Functor, Applicative, Monad) via Identity

Chỉ có một trường hợp hợp lý trong tình huống rất đặc biệt này, nhưng hầu hết thời gian không dễ để nói trường hợp đó là gì ngay cả khi chỉ có một.


Bạn cũng phải lấy FunctorApplicative, nhưng sau đó so sánh loại Container 3 >>= (+1)với Identity 3 >>= (+1). Tôi không biết điều đó có liên quan DerivingViahay không.
chepner

(Trong trường hợp tôi đang làm điều gì đó kỳ lạ, tôi nhận được Container 3 >>= (+ 1) :: Num (Container b) => Container bIdentity 3 >>= (+ 1) :: Num b => Identity btôi không chắc tại sao Container b, thay vì b, có sự Numràng buộc.)
chepner

Cảm ơn sự chính xác. Đối với nhận xét thứ hai của bạn, để có (+ 1) :: Num c => c -> cmột mũi tên Kleisli, (+ 1) :: a -> Container bbạn cần phải thống nhất c ~ Container b. Nhưng tôi không chắc quan điểm của bạn là gì để bắt đầu.
Li-yao Xia

Tôi chỉ tự hỏi những gì được định nghĩa cho Identityđiều đó không được xác định cho Container, như Identity 3 >>= (+1)đánh giá Identity 4.
chepner

Chỉ vì có một instance Num a => Num (Identity a)định nghĩa.
KA Buhr
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.