Non riesco a capire perché questi due frammenti producano risultati diversi sotto la cosiddetta "analisi della severità dei poveri".Pigrizia/rigore tra i dati e newtype
Il primo esempio utilizza data
(ipotizzando una corretta esempio applicativo):
data Parser t a = Parser {
getParser :: [t] -> Maybe ([t], a)
}
> getParser (pure (,) <*> literal ';' <*> undefined) "abc"
*** Exception: Prelude.undefined
La seconda utilizza newtype
. Non c'è altra differenza:
newtype Parser t a = Parser {
getParser :: [t] -> Maybe ([t], a)
}
> getParser (pure (,) <*> literal ';' <*> undefined) "abc"
Nothing
literal x
è un parser che riesce consumando un gettone di ingresso se il suo argomento corrisponde al primo token. Quindi, in questo esempio, non riesce poiché ;
non corrisponde a a
. Tuttavia, l'esempio data
vede ancora che il parser successivo non è definito, mentre l'esempio newtype
non lo fa.
Ho letto this, this e this, ma non li capisco abbastanza bene da capire perché il primo esempio non è definito. Mi sembra che in questo esempio, newtype
è più pigro di data
, il contrario di ciò che le risposte hanno detto. (Almeno one other person è stato confuso da questo anche).
Perché il passaggio da data
a newtype
modifica la definizione di questo esempio?
Ecco un'altra cosa che ho scoperto: a questa istanza applicativa, il data
parser sopra uscite undefined:
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))
che, a questa istanza, il parser data
sopra non non uscita undefined (supponendo un'istanza Monad corretta per Parser s
):
instance Applicative (Parser s) where
f <*> x =
f >>= \f' ->
x >>= \x' ->
pure (f' x')
pure = pure a = Parser (\xs -> Just (xs, a))
codice completo frammento:
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
Quando si fa una domanda come questa, generalmente è meglio se si include tutto il codice pertinente, se è abbastanza piccolo da adattarsi (questo include le istanze 'Functor' e' Monad' e 'literal'), in modo che le persone indossi Non devi indovinare esattamente come hai scritto le funzioni (come hai sottolineato, anche piccole modifiche possono fare la differenza nel comportamento). – shachaf
@shachaf la vera domanda qui non è "come posso risolvere il mio codice?" - L'ho già fatto - ma "cosa c'è di diverso tra' data' e 'newtype' rispetto alla rigidità/pigrizia?" Scusa se questo non è chiaro dalla domanda. –
Sì, ma anche così, come possiamo spiegare cosa stava succedendo con il tuo codice senza sapere come fosse il codice? – shachaf