Tôi đang cố gắng hiểu tại sao hai đoạn này tạo ra các kết quả khác nhau dưới cái gọi là "phân tích độ nghiêm ngặt của người nghèo".Độ mềm/độ chặt giữa dữ liệu và kiểu mới
Ví dụ đầu tiên sử dụng data
(giả sử một trường hợp applicative đúng):
data Parser t a = Parser {
getParser :: [t] -> Maybe ([t], a)
}
> getParser (pure (,) <*> literal ';' <*> undefined) "abc"
*** Exception: Prelude.undefined
Thứ hai sử dụng newtype
. Không có sự khác biệt khác:
newtype Parser t a = Parser {
getParser :: [t] -> Maybe ([t], a)
}
> getParser (pure (,) <*> literal ';' <*> undefined) "abc"
Nothing
literal x
là một phân tích cú pháp mà thành công tiêu thụ một chiếc thẻ đầu vào nếu đối số của nó phù hợp với dấu hiệu đầu tiên. Vì vậy, trong ví dụ này, nó không thành công vì ;
không khớp với a
. Tuy nhiên, ví dụ data
vẫn thấy rằng trình phân tích cú pháp tiếp theo không được xác định, trong khi ví dụ newtype
thì không.
Tôi đã đọc this, this và this, nhưng không hiểu chúng đủ tốt để biết tại sao ví dụ đầu tiên không được xác định. Dường như với tôi rằng trong ví dụ này, newtype
là nhiều hơn lười hơn data
, ngược lại với câu trả lời cho biết. (Ít nhất one other person cũng bị nhầm lẫn bởi điều này).
Tại sao chuyển đổi từ data
thành newtype
thay đổi định nghĩa của ví dụ này?
Đây là một điều tôi khám phá ra: với dụ applicative này, data
phân tích cú pháp trên kết quả đầu ra không xác định:
instance Applicative (Parser s) where
Parser f <*> Parser x = Parser h
where
h xs =
f xs >>= \(ys, f') ->
x ys >>= \(zs, x') ->
Just (zs, f' x')
pure a = Parser (\xs -> Just (xs, a))
trong khi với trường hợp này, phân tích cú pháp data
trên không không sản lượng không xác định (giả sử một bản sao Monad chính xác cho Parser s
):
instance Applicative (Parser s) where
f <*> x =
f >>= \f' ->
x >>= \x' ->
pure (f' x')
pure = pure a = Parser (\xs -> Just (xs, a))
Full đoạn mã:
import Control.Applicative
import Control.Monad (liftM)
data Parser t a = Parser {
getParser :: [t] -> Maybe ([t], a)
}
instance Functor (Parser s) where
fmap = liftM
instance Applicative (Parser s) where
Parser f <*> Parser x = Parser h
where
h xs = f xs >>= \(ys, f') ->
x ys >>= \(zs, x') ->
Just (zs, f' x')
pure = return
instance Monad (Parser s) where
Parser m >>= f = Parser h
where
h xs =
m xs >>= \(ys,y) ->
getParser (f y) ys
return a = Parser (\xs -> Just (xs, a))
literal :: Eq t => t -> Parser t t
literal x = Parser f
where
f (y:ys)
| x == y = Just (ys, x)
| otherwise = Nothing
f [] = Nothing
Khi đặt câu hỏi như thế này, tốt hơn nếu bạn bao gồm tất cả các mã có liên quan, nếu nó đủ nhỏ để vừa (bao gồm trường 'Functor' và' Monad', và 'literal'), để mọi người don không phải đoán chính xác cách bạn viết các hàm (như bạn đã chỉ ra, ngay cả những thay đổi nhỏ có thể tạo ra sự khác biệt về hành vi). – shachaf
@shachaf câu hỏi thực sự ở đây không phải là "làm cách nào để sửa mã của tôi?" - Tôi đã làm điều đó - nhưng "sự khác biệt giữa' dữ liệu' và 'newtype' đối với sự nghiêm khắc/lười biếng là gì?" Xin lỗi nếu điều đó không rõ ràng từ câu hỏi. –
Có, nhưng ngay cả như vậy, làm thế nào chúng ta có thể giải thích những gì đang xảy ra với mã của bạn mà không biết mã trông như thế nào? – shachaf