Staram się zrozumieć, dlaczego te dwa fragmenty dają różne wyniki w ramach tak zwanej "analizy ścisłości człowieka ubogiego".Lenistwo/surowość między danymi a nowym typem
Pierwszy przykład wykorzystuje data
(zakładając właściwe wystąpienie aplikacyjny)
data Parser t a = Parser {
getParser :: [t] -> Maybe ([t], a)
}
> getParser (pure (,) <*> literal ';' <*> undefined) "abc"
*** Exception: Prelude.undefined
Drugi wykorzystuje newtype
. Nie ma innej różnicy:
newtype Parser t a = Parser {
getParser :: [t] -> Maybe ([t], a)
}
> getParser (pure (,) <*> literal ';' <*> undefined) "abc"
Nothing
literal x
jest parser że powiedzie zużywa jeden znak wejścia, jeśli jego argumentem mecze pierwszego tokena. W tym przykładzie kończy się niepowodzeniem, ponieważ ;
nie pasuje do a
. Jednak w przykładzie data
nadal widać, że następny parser jest niezdefiniowany, podczas gdy przykład newtype
nie.
Przeczytałem this, this i this, ale nie rozumiem ich wystarczająco dobrze, aby dowiedzieć się, dlaczego pierwszy przykład jest niezdefiniowany. Wydaje mi się, że w tym przykładzie newtype
jest leniwy niż data
, przeciwieństwo tego, co mówią odpowiedzi. (Przynajmniej przez to też zdezorientowało to one other person).
Dlaczego zmiana z data
na newtype
zmienia definicję tego przykładu?
Oto kolejna rzecz odkryłem: z tym aplikacyjnych przykład data
parser powyżej wyjść niezdefiniowany:
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))
natomiast w tym przypadku data
parser powyżej robi nie wyjście niezdefiniowany (zakładając, poprawna instancja Monada dla Parser s
):
instance Applicative (Parser s) where
f <*> x =
f >>= \f' ->
x >>= \x' ->
pure (f' x')
pure = pure a = Parser (\xs -> Just (xs, a))
Pełna fragment kodu:
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
Kiedy zadajesz takie pytanie, na ogół lepiej jest dołączyć cały odpowiedni kod, jeśli jest wystarczająco mały, aby zmieścić (dotyczy to instancji 'Functor' i' Monad' oraz 'literału'), tak aby ludzie nie musisz dokładnie zgadywać, w jaki sposób pisałeś funkcje (jak już zauważyłeś, nawet niewielkie zmiany mogą mieć wpływ na zachowanie). – shachaf
@ shachaf prawdziwe pytanie tutaj nie jest "jak mogę naprawić mój kod?" - Już to zrobiłem - ale "czym różni się" data "od" newtype "od surowości/lenistwa?" Przepraszam, jeśli to nie wynika z pytania. –
Tak, ale mimo to, jak możemy wyjaśnić, co się dzieje z Twoim kodem, nie wiedząc, jak wygląda kod? – shachaf