¿Cómo se establece un patrón de expresiones regulares en Haskell?

Nov 28 2020

Estoy tratando de hacer un reemplazo de expresiones regulares con el siguiente código

import Text.RE.Replace
import Text.RE.TDFA.String

onlyLetters :: String -> String
onlyLetters s = replaceAll "" $ s *=~ [re|$([^a-zA-Z])|]

Me resultó muy difícil encontrar documentación comprensible sobre esto. Esto produce el error de compilación:

    src\Pangram.hs:6:53: error: parse error on input `]'
  |
6 | onlyLetters s = replaceAll "" $ (s *=~ [re|[a-zA-Z]|]) | ^ Progress 1/2 -- While building package pangram-2.0.0.12 (scroll up to its section to see the error) using: C:\sr\setup-exe-cache\x86_64-windows\Cabal-simple_Z6RU0evB_3.0.1.0_ghc-8.8.4.exe --builddir=.stack-work\dist\29cc6475 build lib:pangram test:test --ghc-options " -fdiagnostics-color=always" Process exited with code: ExitFailure 1 PS C:\Users\mcleg\Exercism\haskell\pangram> stack test pangram> configure (lib + test) Configuring pangram-2.0.0.12... pangram> build (lib + test) Preprocessing library for pangram-2.0.0.12.. Building library for pangram-2.0.0.12.. [1 of 2] Compiling Pangram src\Pangram.hs:7:56: error: parse error on input `]' | 7 | onlyLetters s = replaceAll "" $ s *=~ [re|$([^a-zA-Z])|]
  |                                                        ^

Progress 1/2

--  While building package pangram-2.0.0.12 (scroll up to its section to see the error) using:
      C:\sr\setup-exe-cache\x86_64-windows\Cabal-simple_Z6RU0evB_3.0.1.0_ghc-8.8.4.exe --builddir=.stack-work\dist\29cc6475 build lib:pangram test:test --ghc-options " -fdiagnostics-color=always"
    Process exited with code: ExitFailure 1

¿Cuál es el problema con ese soporte y cómo lo haría correctamente? Gracias -Skye

Respuestas

3 WillemVanOnsem Nov 28 2020 at 18:56

El […|…|]es la cita cuasi sintaxis [Haskell-wiki] . Esta es una extensión de la sintaxis de Haskell y no está habilitada de forma predeterminada.

Puede activar esto con un LANGUAGEpragma:

{-# LANGUAGE QuasiQuotes #-}

import Text.RE.Replace
import Text.RE.TDFA.String

onlyLetters :: String -> String
onlyLetters s = replaceAll "" $ s *=~ [re|$([^a-zA-Z])|]

Las cuasicuotas generarán código Haskell y luego se utilizará en el programa Haskell. Esto significa que a través de las cuasícuotas, la validación de la expresión regular se puede realizar en tiempo de compilación y podría incluso optimizar ligeramente la eficiencia en comparación con la compilación de expresiones regulares en tiempo de ejecución.

Para la onlyLettersfunción dada , obtenemos:

*Main> onlyLetters "fo0b4r"
"fobr"
2 JamesBrock Nov 29 2020 at 21:02

La respuesta de Willem Van Onsem es una mejor respuesta a la pregunta, pero voy a sugerir una respuesta de "prueba esto en su lugar" .

Así es como puede hacer el reemplazo de texto en Haskell simple sin la complicación de las expresiones regulares entre comillas.

Con https://hackage.haskell.org/package/replace-megaparsec/docs/Replace-Megaparsec.html#v:streamEdit

{-# LANGUAGE TypeFamilies #-}

import Text.Megaparsec
import Text.Megaparsec.Char
import Replace.Megaparsec
import Data.Void

-- | Invert a single-token parser “character class”.
-- | For example, match any single token except a letter or whitespace: `anySingleExcept (letterChar <|> spaceChar)`
anySingleExcept :: (MonadParsec e s m, Token s ~ Char) => m (Token s) -> m (Token s)
anySingleExcept p = notFollowedBy p *> anySingle

-- | A parser monad pattern which matches anything except letters.
nonLetters :: Parsec Void String String
nonLetters = many (anySingleExcept letterChar) 

onlyLetters :: String -> String
onlyLetters = streamEdit nonLetters (const "")

onlyLetters "fo0b4r"
"fobr"