Sto lavorando attraverso lo Brent Yorgey Haskell course e sto riscontrando problemi nella definizione di una buona istanza per Applicative. Un parser è definito come segue:Migliore istanza applicativa per Parser (Haskell)
newtype Parser a = Parser { runParser :: String -> Maybe (a, String) }
La funzione accetta una stringa, analizza una certa quantità di input e restituisce un forse tupla in cui il primo valore è il tipo del parser, e il resto è il resto unparsed della stringa. Ad esempio, questo è un parser per i numeri interi positivi:
posInt :: Parser Integer
posInt = Parser f
where
f xs
| null ns = Nothing
| otherwise = Just (read ns, rest)
where (ns, rest) = span isDigit xs
L'assegnazione è quello di rendere un caso applicativo per Parser. Iniziamo con un'istanza Functor (che è relativamente straight-forward, credo):
first :: (a -> b) -> (a,c) -> (b,c)
first f (a, c) = (f a, c)
instance Functor Parser where
fmap f p = Parser f'
where f' s = fmap (first f) $ (runParser p) s
E poi ho provato la mia mano con applicativo:
collapse (Just (Just a)) = Just a
collapse _ = Nothing
extract (Just a, Just b) = Just (a,b)
extract _ = Nothing
appliedFunc :: Parser (a->b) -> Parser a -> String -> Maybe (b, String)
appliedFunc p1 p2 str = extract (f <*> fmap fst result2, fmap snd result2)
where result1 = (runParser p1) str
f = fmap fst result1
result2 = collapse $ fmap (runParser p2) $ fmap snd result1
instance Applicative Parser where
pure a = Parser (\s -> Just (a, s))
p1 <*> p2 = Parser (appliedFunc p1 p2)
... bleah. Quindi la mia domanda è: come posso rendere la mia istanza applicativa più pulita e meno difficile da leggere? Mi sembra che ci sia una risposta facile a questa domanda, ma non sono ancora riuscito a capire come funziona.
@AndrewC Per un esercizio potresti avere ragione, ma c'è un problema più profondo relativo alla risposta di Gabriel Gonzalez: 'StateT m' non è un' Applicativo' a meno che 'm' sia un pieno' Monad'. Questo varia tra i trasformatori: 'MaybeT m' richiede anche un pieno' Monade', 'ReaderT m' e' WriterT m' richiedono solo 'Applicativo', mentre' ContT m' è famoso per ottenere un pieno 'Monade' con * no * requisiti su 'm'. –
Le risposte di Gabriel Gonzalez sono sempre eccellenti, e avevo già anche svalutato la sua. È una pubblicità avvincente per i trasformatori monad che questo codice è così sintetico e chiaro. Naturalmente, come hai giustamente sottolineato, l'errore nel mio commento era a che livello si stava svolgendo il join, causandomi non poco imbarazzo soprattutto dal momento che avevo spiegato precedentemente ad un altro interrogatore perché la monade era necessaria all'interno del parser quando volevano usare solo applicativo per tutto! Doh! – AndrewC