traductor traducir traduccion tag name mundo ingles frances español buscar app haskell monads applicative

haskell - traducir - traductor español frances



Traducir de la mónada al aplicativo. (3)

Su ejemplo puede reescribirse progresivamente a una forma que se asemeja más claramente a un Aplicativo:

do many1 space ds <- many1 digit return $ read ds

  1. definición de notación do :

    many1 space >> (many1 digit >>= /ds -> return $ read ds)

  2. definición de $ :

    many1 space >> (many1 digit >>= /ds -> return (read ds))

  3. definición de . :

    many1 space >> (many1 digit >>= (return . read))

  4. 3ª ley de la mónada (asociatividad):

    (many1 space >> many1 digit) >>= (return . read)

  5. definición de liftM (en notación liftM - do ):

    liftM read (many1 space >> many1 digit)

Esto es (o debería ser, si no me he equivocado :)) de comportamiento idéntico a tu ejemplo.

Ahora, si reemplaza liftM con fmap con <$> , y >> con *> , obtendrá el Aplicativo:

read <$> (many1 space *> many1 digit)

Esto es válido porque liftM , fmap y <$> generalmente se supone que son sinónimos, como son >> y *> .

Todo esto funciona y podemos hacerlo porque el ejemplo original no usó el resultado de ningún analizador para crear un analizador siguiente.

OK, así que sé lo que contiene la clase de tipo Applicative y por qué es útil. Pero no puedo delimitar completamente mi cerebro sobre cómo lo usarías en un ejemplo no trivial.

Considere, por ejemplo, el siguiente analizador Parsec bastante simple:

integer :: Parser Integer integer = do many1 space ds <- many1 digit return $ read ds

Ahora, ¿cómo diablos escribirías eso sin usar la instancia de Parser para Parser ? Mucha gente afirma que esto se puede hacer y es una buena idea, pero no puedo entender cómo exactamente.


Yo escribo

integer :: Parser Integer integer = read <$ many1 space <*> many1 digit

Hay un grupo de operadores de creación de analizadores asociativos (como aplicaciones) a la izquierda <$> , <*> , <$ , <* . La cosa en el extremo izquierdo debe ser la función pura que ensambla el valor del resultado de los valores del componente. La cosa a la derecha de cada operador debe ser un analizador, dando colectivamente los componentes de la gramática de izquierda a derecha. El operador a utilizar depende de dos opciones, de la siguiente manera.

the thing to the right is signal / noise _________________________ the thing to the left is / +------------------- pure / | <$> <$ a parser | <*> <*

Entonces, habiendo elegido read :: String -> Integer como la función pura que entregará la semántica del analizador, podemos clasificar el espacio inicial como "ruido" y el grupo de dígitos como "señal", por lo tanto

read <$ many1 space <*> many1 digit (..) (.........) (.........) pure noise parser | (.................) | parser signal parser (.................................) parser

Puedes combinar múltiples posibilidades con

p1 <|> ... <|> pn

y expresa imposibilidad con

empty

Rara vez es necesario nombrar componentes en analizadores, y el código resultante se parece más a una gramática con semántica agregada.


integer :: Parser Integer integer = read <$> (many1 space *> many1 digit)

O

integer = const read <$> many1 space <*> many1 digit

Si crees que cualquiera de estos son más legibles depende de ti.