2017-07-21 21 views
6

Tôi đang học máy biến áp đơn nguyên và tôi bối rối khi sử dụng thang máy là cần thiết. Giả sử rằng tôi có các mã sau đây (Nó không làm bất cứ điều gì thú vị, chỉ đơn giản nhất tôi có thể đi kèm với để trình diễn).Khi chính xác là cần thiết trong máy biến áp monad?

foo :: Int -> State Int Int 
foo x = do 
    (`runContT` pure) $ do 
    callCC $ \exit -> do 
     when (odd x) $ do 
     -- lift unnecessary 
     a <- get 
     put $ 2*a 
     when (x >= 5) $ do 
     -- lift unnecessary, but there is exit 
     a <- get 
     exit a 
     when (x < 0) $ do 
     -- lift necessary 
     a <- lift $ foo (x + 10) 
     lift $ put a 

     lift get 

Vì vậy, có một ngăn xếp đơn nguyên, nơi khối chính có loại ContT Int (StateT Int Identity) Int.

Bây giờ, trong phần thứ ba when làm chặn với đệ quy cần phải có thang máy để chương trình biên dịch. Trong khối thứ hai, không có thang máy cần thiết, nhưng tôi bằng cách nào đó giả định đó là vì sự hiện diện của exit mà bằng cách nào đó buộc dòng trên được nâng lên ContT. Nhưng trong khối đầu tiên, không cần có thang máy. (Nhưng nếu nó được thêm vào một cách rõ ràng thì cũng chẳng có vấn đề gì cả.) Điều này thực sự gây nhầm lẫn cho tôi. Tôi cảm thấy tất cả các khối when làm tương đương và cả thang máy nên được yêu cầu ở khắp mọi nơi hoặc không nơi nào. Nhưng điều đó dường như không đúng. Đâu là sự khác biệt chính khiến thang máy yêu cầu/không cần thiết?

Trả lời

11

Sự nhầm lẫn ở đây phát sinh do thư viện biến áp đơn lẻ bạn đang sử dụng đang được thông minh một chút. Cụ thể, loại getput không đề cập rõ ràng State hoặc StateT. Thay vào đó, họ dọc theo dòng

get :: MonadState s m => m s 
put :: MonadState s m => s -> m() 

Vì vậy chừng nào chúng ta sử dụng điều này trong một bối cảnh với một MonadState đơn nguyên thực hiện không có nhu cầu rõ ràng lift s. Đây là trường hợp trong tất cả các trường hợp mà bạn sử dụng get/put từ

instance MonadState s (StateT s m) 
instance MonadState s m => ContT k m 

cả giữ. Nói cách khác, độ phân giải lớp loại sẽ tự động xử lý thực hiện việc nâng thích hợp cho bạn. Điều này lần lượt ngụ ý rằng bạn có thể tách số lift s trên get/put vào cuối chương trình của bạn.

Điều này không thể xảy ra với các cuộc gọi đệ quy của bạn vì loại cuộc gọi rõ ràng là State Int Int. Nếu bạn đã khái quát hóa nó thành MonadState Int m => m Int, bạn thậm chí có thể bỏ qua thang máy cuối cùng này.

6

Tôi muốn cung cấp một câu trả lời thay thế vừa là bề ngoài vừa đồng thời mọi thứ đều quan trọng.

Bạn cần sử dụng lift khi lift kiểm tra loại mọi thứ nếu không.

Có, âm thanh bề ngoài và dường như thiếu ý nghĩa sâu sắc. Nhưng điều đó không hoàn toàn đúng. MonadTrans là một lớp học cho những thứ có thể nâng các hành động đơn thuần vào một ngữ cảnh lớn hơn theo cách trung lập. Luật lớp học cung cấp các quy tắc rõ ràng hơn về những gì "trung lập" có nghĩa là, nếu bạn muốn có một mô tả kỹ thuật. Nhưng kết quả là lift không làm gì ngoài những gì cần thiết để làm cho hành động được cung cấp tương thích với loại khác.

Vì vậy - lift làm gì? Nó cung cấp logic cần thiết để nâng một hành động đơn thuần thành một loại lớn hơn. Khi nào bạn cần sử dụng? Khi bạn có một hành động monadic mà bạn cần phải nâng lên thành một loại lớn hơn. Khi nào bạn có một hành động monadic mà bạn cần phải nâng lên thành một loại lớn hơn? Khi đó là những gì các loại cho bạn biết.

Đây là phần quan trọng trong việc sử dụng Haskell.Bạn có thể mô đun hóa sự hiểu biết của bạn về mã. Các loại hệ thống theo dõi một số lượng lớn của sổ sách kế toán cho bạn. Dựa vào nó để có được sổ kế toán đúng, vì vậy bạn chỉ cần giữ logic trong đầu của bạn. Trình biên dịch và hệ thống kiểu là có để làm việc như bộ khuếch đại tinh thần. Họ càng chăm sóc, bạn càng ít phải giữ đầu trong khi viết phần mềm.

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