no puedo almacenar los datos globales en cualquier lugar con Haskell
esto no es cierto; en la mayoría de los casos es suficiente algo como el State monad, pero también está el ST monad.
Sin embargo, no necesita un estado global para esta tarea. Escribir un analizador consiste en dos partes; análisis léxico y análisis de sintaxis. El análisis léxico solo convierte una secuencia de caracteres en una secuencia de tokens significativos. El análisis de sintaxis convierte tokens en un AST; aquí es donde debes lidiar con la sangría.
Mientras está interpretando la sangría, llamará a una función del manejador cuando cambie el nivel de sangría: cuando aumenta (anidamiento), llama a la función del manejador (quizás con un arg incrementado, si desea rastrear la sangría nivel); cuando el nivel disminuye, simplemente devuelve la parte relevante de AST de la función.
(Como un lado, el uso de una variable global para esto es algo que tampoco se me ocurriría en un lenguaje imperativo - en todo caso, es una variable de instancia. La mónada de Estado es muy similar conceptualmente a esto.)
Finalmente, creo que la frase "No puedo poner todas mis reglas de lexing dentro de una mónada" indica algún tipo de malentendido de las mónadas. Si necesitaba para analizar y mantener el estado global, el código se vería así:
data AST = ...
type Step = State Int AST
parseFunction :: Stream -> Step
parseFunction s = do
level <- get
...
if anotherFunction then put (level + 1) >> parseFunction ...
else parseWhatever
...
return node
parse :: Stream -> Step
parse s = do
if looksLikeFunction then parseFunction ...
main = runState parse 0 -- initial nesting of 0
En lugar de la combinación de aplicaciones de funciones con (.)
o ($)
, se combinan con (>>=)
o (>>)
. Aparte de eso, el algoritmo es el mismo. (No hay una "mónada" para estar "dentro".)
Por último, que le gustaría funtores aplicativos:
eval :: Environment -> Node -> Evaluated
eval e (Constant x) = Evaluated x
eval e (Variable x) = Evaluated (lookup e x)
eval e (Function f x y) = (f <$> (`eval` x) <*> (`eval` y)) e
(o
eval e (Function f x y) = ((`eval` f) <*> (`eval` x) <*> (`eval` y)) e
si tiene algo así como "funcall" ... pero estoy divagando.)
Hay mucha literatura sobre el análisis con funcionadores aplicativos, mónadas y flechas; todos los cuales tienen el potencial de resolver su problema. Lea sobre eso y vea lo que obtiene.
Agradable. Terminé jugando un poco con Alex y descubrí que es más limpio en algunos aspectos que PArrows (que normalmente es a lo que recurro). Gracias por la información :) – jrockway
Ah, gracias por esto. También pregunté por #haskell, descubrí el contenedor de UserState para alex. Sin embargo, no hay muchos documentos sobre esto, tuvimos que buscar un poco de fuente. Sé acerca de la mónada de estado, pero no estaba seguro de enhebrar el estado a través del lector de Alex. Gracias por la ayuda. – kamatsu