2011-10-20 33 views
7

Tôi xác định một thể hiện của một đơn nguyên như sau:Haskell có luôn biết 'quay lại' nào để gọi?

data Something = Something a 

instance Monad Something where 
    return a = Something a  --Wraps a in 'Something', correct? 
    m >>= f = do 
     var <- m 
     return $ f var   --I want this to pass var to f, then wrap the result 
            --back up in the 'Something' monad with the return I 
            --Just defined 

Các câu hỏi là ->

1: Có bất kỳ lỗi rõ ràng/quan niệm sai lầm với những gì tôi đang làm gì thế?

2: Haskell sẽ biết phải gọi sự trở lại tôi đã định nghĩa ở trên từ m >>= f

3: Nếu tôi vì một lý do xác định một chức năng

f :: Something a -> Something b 
f x = do 
    var <- x 
    return $ doMagicTo x 

Will return gọi sự trở lại tôi định nghĩa trong đơn nguyên ví dụ và bọc x trong Something?

Trả lời

15

Có một vài vấn đề lớn ở đây.

Trước tiên, phiên bản Monad phải có kind* -> *. Điều đó có nghĩa là họ cần ít nhất một biến số , trong đó Something của bạn không có bất kỳ biến nào. Để so sánh:

-- kind * -> * 
Maybe 
IO 
Either String 

-- kind * 
Maybe Int 
IO() 
Either String Double 

Xem cách mỗi Maybe, IO, và Either String cần một tham số kiểu trước khi bạn có thể sử dụng chúng? Với Something, không có chỗ cho các tham số kiểu để điền vào Vì vậy, bạn cần phải thay đổi định nghĩa của bạn để:.

data Something a = Something a 

Vấn đề lớn thứ hai là các >>= trong trường hợp đơn nguyên của bạn là sai. Nói chung, bạn không thể sử dụng ký hiệu vì nó chỉ gọi các hàm Monadreturn>>=. Vì vậy, bạn phải viết nó ra mà không có bất kỳ chức năng monad, hoặc là do-notation hoặc gọi >>= hoặc return.

instance Monad Something where 
    return a = Something a  --Wraps a in 'Something' 
    (Something m) >>= f = f m  --unwraps m and applies it to f 

Định nghĩa của >>= đơn giản hơn bạn mong đợi. Unwrapping m thật dễ dàng vì bạn chỉ cần khớp mẫu trên hàm tạo Something. Ngoài ra f :: a -> m b, vì vậy bạn không cần phải lo lắng về việc gói nó lại, bởi vì f thực hiện điều đó cho bạn.

Trong khi không có cách nào để mở một đơn lẻ nói chung, rất nhiều cụ thể đơn lẻ có thể được bỏ.

Lưu ý rằng không có gì sai về cú pháp khi sử dụng ký hiệu hoặc >>= trong khai báo cá thể đơn lẻ. Vấn đề là >>= được định nghĩa đệ quy để chương trình đi vào vòng lặp vô tận khi bạn cố gắng sử dụng nó.

(N.B. Something như định nghĩa ở đây là Identity monad)

Đối với câu hỏi thứ ba của bạn, có những return chức năng được xác định trong trường hợp đơn nguyên là một trong đó sẽ được gọi. Loại lớp được gửi theo loại, và như bạn đã chỉ định loại phải là Something b trình biên dịch sẽ tự động sử dụng dụ Monad cho Something. (Tôi nghĩ bạn có nghĩa là dòng cuối cùng là doMagicTo var).

+3

Điều này thật tuyệt. Tất cả những câu trả lời này đều rất tốt. Tôi cảm thấy bắt buộc phải nhận xét rằng cộng đồng Haskell cho đến nay là hữu ích và kỹ lưỡng nhất đối với bất kỳ tôi đã gặp phải. Cố lên các chàng trai. – providence

2
  1. Đóng. Các return là dư thừa ở đây và bạn cần phải thêm một tham số kiểu để xây dựng loại Something của bạn. Chỉnh sửa: Mã này vẫn sai. Định nghĩa của >>= là hình tròn. Xem các câu trả lời khác để biết thêm thông tin.

    data Something a = Something a 
    
    instance Monad Something where 
        return a = Something a 
        m >>= f = do 
         var <- m 
         f var 
    
  2. Từ định nghĩa của bạn cho >>= là dưới instance Monad Something where, gõ >>= 's là Something a -> (a -> Something b) -> Something b. Vì vậy, có thể nói rằng f var phải thuộc loại Something b. Thuật ngữ cho google ở ​​đây là "loại suy luận"

  3. Có. Một lần nữa, đây là suy luận kiểu. Nếu trình biên dịch không thể suy ra loại bạn muốn, nó sẽ cho bạn biết như vậy. Nhưng thông thường nó có thể.

+0

Xem giải pháp của John L: bạn không thể sử dụng lệnh do-block để xác định '>> =' như vậy, bởi vì bạn nghĩ khối do bị loại bỏ như thế nào? (Cũng được nêu trong giải pháp của hung hăng.) – ivanm

+0

D'oh, gọi tốt. Tôi phải nghĩ nhiều hơn như một lập trình viên Haskell bây giờ, vì tôi vừa lấy mã của OP, sửa đổi nó cho đến khi nó được kiểm tra và biên dịch, và nói, "Yup, điều đó phải đúng." : P Tuy nhiên, tôi nghĩ câu hỏi của OP là nhiều hay ít "Liệu Haskell có suy luận kiểu không?" mặc dù họ không hỏi theo cách đó. Và điểm chính của tôi là, đúng vậy. – MatrixFrog

7

Vấn đề chính là định nghĩa của bạn là >>= là hình tròn.

Haskell làm cú pháp là đường syntatic cho chuỗi >>=>>, vì vậy định nghĩa của bạn

m >>= f = do 
    var <- m 
    return $ f var   

Desugars để

m >>= f = 
    m >>= \var -> 
    return $ f var 

Vì vậy, bạn đang xác định m >>= f như m >>= \..., đó là hình tròn.

Những gì bạn cần làm với >>= là xác định cách trích xuất một giá trị từ m để chuyển đến f. Ngoài ra, f của bạn phải trả lại một giá trị monadic, do đó, sử dụng return là sai ở đây (điều này gần với cách bạn muốn xác định fmap).

Một định nghĩa của >>= cho Something có thể là:

(Something a) >>= f = f a 

Đây là Identity Monad - có một thỏa thuận tốt bằng văn bản về nó - đó là một điểm khởi đầu tốt để hiểu cách monads làm việc.

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