Đỗ-ký hiệu desugars đến (>> =) cú pháp theo cách này:
getPerson = do
name <- getLine -- step 1
age <- getInt -- step 2
return $ Just Person <*> Just name <*> age
getPerson2 =
getLine >>=
(\name -> getInt >>=
(\age -> return $ Just Person <*> Just name <*> age))
mỗi dòng trong do-ký hiệu, sau khi lần đầu tiên, được dịch sang một lambda sau đó được ràng buộc với dòng trước đó . Đó là một quá trình hoàn toàn cơ học để liên kết các giá trị với tên. Tôi không thấy cách sử dụng ký hiệu hay không sẽ ảnh hưởng đến khả năng tổng hợp; nó hoàn toàn là vấn đề cú pháp.
chức năng khác của bạn cũng tương tự như:
getInt :: IO (Maybe Int)
getInt = do
n <- fmap reads getLine :: IO [(Int,String)]
case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
getInt2 :: IO (Maybe Int)
getInt2 =
(fmap reads getLine :: IO [(Int,String)]) >>=
\n -> case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
Một vài gợi ý cho việc định hướng bạn dường như đứng đầu:
Khi sử dụng Control.Applicative
, nó thường hữu ích để sử dụng <$>
để nâng chức năng tinh khiết vào đơn nguyên . Có một cơ hội tốt cho điều này trong dòng cuối cùng:
Just Person <*> Just name <*> age
trở thành
Person <$> Just name <*> age
Ngoài ra, bạn nên nhìn vào máy biến áp đơn nguyên. Gói mtl phổ biến nhất vì nó đi kèm với Nền tảng Haskell, nhưng có các tùy chọn khác. Biến thế Monad cho phép bạn tạo ra một đơn nguyên mới với hành vi kết hợp của các monads cơ bản. Trong trường hợp này, bạn đang sử dụng các hàm có loại IO (Maybe a)
. Các mtl (thực sự là một thư viện cơ sở, máy biến áp) định nghĩa
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
Đây là giống như kiểu bạn đang sử dụng, với m
biến instantiated tại IO
.Điều này có nghĩa bạn có thể viết:
getPerson3 :: MaybeT IO Person
getPerson3 = Person <$> lift getLine <*> getInt3
getInt3 :: MaybeT IO Int
getInt3 = MaybeT $ do
n <- fmap reads getLine :: IO [(Int,String)]
case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
getInt3
là giống hệt nhau ngoại trừ cho các nhà xây dựng MaybeT
. Về cơ bản, bất cứ khi nào bạn có m (Maybe a)
, bạn có thể bọc nó trong MaybeT
để tạo một MaybeT m a
. Điều này có khả năng tương thích đơn giản hơn, như bạn có thể thấy theo định nghĩa mới của getPerson3
. Chức năng đó không lo lắng về sự thất bại vì tất cả đều được xử lý bởi hệ thống ống nước của MightT. Một phần còn lại là getLine
, chỉ là IO String
. Điều này được đưa vào đơn vị MightT bằng hàm lift
.
Chỉnh sửa nhận xét của newacct gợi ý rằng tôi cũng nên cung cấp ví dụ mẫu phù hợp; về cơ bản nó giống với một ngoại lệ quan trọng. Hãy xem xét ví dụ này (danh sách đơn nguyên là đơn nguyên chúng tôi quan tâm, Maybe
là chỉ có cho phù hợp với mô hình):
f :: Num b => [Maybe b] -> [b]
f x = do
Just n <- x
[n+1]
-- first attempt at desugaring f
g :: Num b => [Maybe b] -> [b]
g x = x >>= \(Just n) -> [n+1]
Đây g
thực hiện chính xác những điều tương tự như f
, nhưng những gì nếu mô hình phù hợp không?
Prelude> f [Nothing]
[]
Prelude> g [Nothing]
*** Exception: <interactive>:1:17-34: Non-exhaustive patterns in lambda
Điều gì đang xảy ra? Trường hợp cụ thể này là lý do cho một trong những mụn cóc lớn nhất (IMO) trong Haskell, phương pháp fail
của lớp học Monad
. Trong ký hiệu, khi một mẫu khớp không thành công fail
được gọi. Một dịch thực tế sẽ được gần gũi hơn với:
g' :: Num b => [Maybe b] -> [b]
g' x = x >>= \x' -> case x' of
Just n -> [n+1]
_ -> fail "pattern match exception"
bây giờ chúng tôi có
Prelude> g' [Nothing]
[]
fail
s hữu phụ thuộc vào đơn nguyên. Đối với các danh sách, nó cực kỳ hữu ích, về cơ bản làm cho công việc phù hợp với mô hình trong việc hiểu danh sách. Nó cũng rất tốt trong đơn Maybe
, vì lỗi trùng khớp mẫu sẽ dẫn đến tính toán không thành công, chính xác là khi Maybe
phải là Nothing
. Đối với IO
, có lẽ không quá nhiều, vì nó chỉ đơn giản là ném một ngoại lệ lỗi người dùng thông qua error
.
Đó là toàn bộ câu chuyện.
Xem thêm: [Báo cáo Haskell 2010> Biểu thứC# Làm biểu thức] (http://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-470003.14) –
Chỉ cần để nitpick, lưu ý rằng 'getPerson' isn 't một hàm, vì nó không có '->' trong kiểu chữ ký của nó; nếu bạn muốn một tên chính xác hơn "giá trị", tôi sẽ đi với "hành động IO". Xem ["Mọi thứ đều là một hàm" trong Haskell?] (Http://conal.net/blog/posts/everything-is-a-function-in-haskell) để biết thêm về điều này. –