Entrada e saída e parsers com combinadores
Ações em IO com do e um parser de expressões com precedência feito de combinadores.
Ações e valores em IO
getLine :: IO Stringé uma ação, não a string lida.putStrLn :: String -> IO ()cria uma ação de impressão.IO ()devolve(); pode imprimir ou nem produzir interação observável, comopure ().
main :: IO ()
main = do
nome <- getLine
let cumprimento = "Ola, " ++ nome ++ "!"
putStrLn cumprimento
<- corre a ação e nomeia o resultado; let nomeia um cálculo puro. As ações correm por ordem. return n cria uma ação que devolve n; não interrompe o bloco como um return imperativo. length getLine é um erro de tipo: uma ação não é o valor que produz.
Parsers como funções
newtype Parser a = P (String -> [(a,String)])
Um parser lê um prefixo e devolve o valor com o resto. Nesta lição, [] é falha e [(v,resto)] é o único sucesso; o tipo permite vários pares, mas os combinadores apresentados pressupõem determinismo.
| Operação | Comportamento |
|---|---|
item | Lê um caráter: "abc" dá [('a',"bc")]; entrada vazia falha. |
pure v | Devolve v sem consumir entrada. |
fmap f p | Transforma o valor, conservando o resto. |
p >>= f | Usa o valor para escolher o parser seguinte, corrido no resto. |
p +++ q | Se p falhar, tenta q na entrada original; prefere o sucesso de p. |
many p, many1 p | Zero ou mais, ou uma ou mais ocorrências. |
token p | Ignora espaços à volta de p. |
parse nat "42x" lê 4, depois 2, e dá [(42,"x")]. nat :: Parser Int pressupõe que o valor cabe em Int; para naturais arbitrariamente grandes, muda o resultado para Integer.
Precedência, associatividade e progresso
A gramática é expr = term '+' expr | term, term = factor '*' term | factor, factor = '(' expr ')' | nat.
termagrupa produtos antes deexpros somar:2+3*4dá14;(2+3)*4dá20.- A recursão à direita associa à direita. Com subtração em vez de adição,
8-3-1daria8-(3-1)=6; acumular à esquerda daria4. Prioridade e associatividade são escolhas separadas. - Recursão à esquerda direta (
expr = expr '+' term) chama o parser sem consumir nada e pode divergir. many pexige que cada sucesso consuma entrada.many (pure 1)repete sem avançar.
Validar a entrada inteira
O sucesso de um prefixo não valida a entrada. parse expr "2+3x" deixa "x"; "2+" recua para o prefixo 2 e deixa "+". Uma aplicação que exige uma expressão inteira só aceita [(v,"")], depois de consumir espaços:
completa :: Parser a -> String -> Maybe a
completa p texto = case parse (token p) texto of
[(valor, "")] -> Just valor
_ -> Nothing
Aceita " 2 + 3*4 " com Just 14; rejeita "2+", "2+3x" e "(2+3". Este retrocesso é o contrato de +++ desta lição; outras bibliotecas distinguem falha antes e depois do consumo.