utf8 lectura escritura archivos unicode haskell formatting locale ghci

unicode - utf8 - lectura y escritura de archivos en python



¿Cómo hackear GHCi(o Abrazos) para que imprima caracteres Unicode sin guardar? (7)

Mire el problema: normalmente, en el entorno interactivo Haskell, los caracteres Unicode no latinos (que forman parte de los resultados) se imprimen con escapes, incluso si la configuración regional permite tales caracteres (en oposición a la salida directa a través de putStrLn , putChar que se ve bien y legible): los ejemplos muestran GHCi y Hugs98:

$ ghci GHCi, version 7.0.1: http://www.haskell.org/ghc/ :? for help Prelude> "hello: привет" "hello: /1087/1088/1080/1074/1077/1090" Prelude> ''Я'' ''/1071'' Prelude> putStrLn "hello: привет" hello: привет Prelude> :q Leaving GHCi. $ hugs -98 __ __ __ __ ____ ___ _________________________________________ || || || || || || ||__ Hugs 98: Based on the Haskell 98 standard ||___|| ||__|| ||__|| __|| Copyright (c) 1994-2005 ||---|| ___|| World Wide Web: http://haskell.org/hugs || || Bugs: http://hackage.haskell.org/trac/hugs || || Version: September 2006 _________________________________________ Hugs mode: Restart with command line option +98 for Haskell 98 mode Type :? for help Hugs> "hello: привет" "hello: /1087/1088/1080/1074/1077/1090" Hugs> ''Я'' ''/1071'' Hugs> putStrLn "hello: привет" hello: привет Hugs> :q [Leaving Hugs] $ locale LANG=ru_RU.UTF-8 LC_CTYPE="ru_RU.UTF-8" LC_NUMERIC="ru_RU.UTF-8" LC_TIME="ru_RU.UTF-8" LC_COLLATE="ru_RU.UTF-8" LC_MONETARY="ru_RU.UTF-8" LC_MESSAGES="ru_RU.UTF-8" LC_PAPER="ru_RU.UTF-8" LC_NAME="ru_RU.UTF-8" LC_ADDRESS="ru_RU.UTF-8" LC_TELEPHONE="ru_RU.UTF-8" LC_MEASUREMENT="ru_RU.UTF-8" LC_IDENTIFICATION="ru_RU.UTF-8" LC_ALL= $

Podemos adivinar que se debe a que print y show se usan para formatear el resultado, y estas funciones hacen todo lo posible para formatear los datos de una manera canónica y máximamente portátil, por lo que prefieren escapar de los caracteres extraños (quizás, incluso se detalla) en un estándar para Haskell):

$ ghci GHCi, version 7.0.1: http://www.haskell.org/ghc/ :? for help Prelude> show ''Я'' "''//1071''" Prelude> :q Leaving GHCi. $ hugs -98 Type :? for help Hugs> show ''Я'' "''//1071''" Hugs> :q [Leaving Hugs] $

Pero aún sería bueno si supiéramos cómo hackear GHCi o Hugs para imprimir estos caracteres de una manera bastante legible para los humanos, es decir, directamente, sin escabullirse. Esto se puede apreciar cuando se usa el entorno interactivo Haskell con fines educativos, para un tutorial / demostración de Haskell frente a un público no inglés al que se le quiere mostrar algo de Haskell en su lenguaje humano.

¡En realidad, no solo es útil para fines educativos sino también para la depuración! Cuando tiene funciones definidas en cadenas que representan palabras de otros idiomas, con caracteres que no son ASCII. Entonces, si el programa es específico del idioma, y ​​solo las palabras de otro idioma tienen sentido como los datos, y usted tiene funciones que están definidas solo en tales palabras, es importante para la depuración en GHCi ver estos datos.

Para resumir mi pregunta: ¿Qué formas de hackear los entornos interactivos Haskell existentes para una impresión más amigable de Unicode en los resultados están ahí? ("Más amistoso" significa incluso "más simple" en mi caso: me gustaría print en GHCi o Abrazos para mostrar caracteres no latinos de la manera directa y simple como lo hacen putChar , putStrLn , es decir, sin putChar ).

(Quizás, además de GHCi y Hugs98, también echaré un vistazo a los modos existentes de Emacs para interactuar con Haskell y ver si pueden presentar los resultados de una manera bonita, sin escabullirse).


Opción 1 (mala):

Modificar esta línea de código:

https://github.com/ghc/packages-base/blob/ba98712/GHC/Show.lhs#L356

showLitChar c s | c > ''/DEL'' = showChar ''//' (protectEsc isDec (shows (ord c)) s)

Y recompilar ghc.

Opción 2 (mucho trabajo):

Cuando el tipo GHCi comprueba una declaración analizada, termina en tcRnStmt que se basa en mkPlan (ambos en https://github.com/ghc/ghc/blob/master/compiler/typecheck/TcRnDriver.lhs ). Esto intenta escribir compruebe varias variantes de la instrucción que se escribió incluyendo:

let it = expr in print it >> return [coerce HVal it]

Específicamente:

print_it = L loc $ ExprStmt (nlHsApp (nlHsVar printName) (nlHsVar fresh_it)) (HsVar thenIOName) placeHolderType

Todo lo que podría necesitar cambiar aquí es printName (que se une a System.IO.print ). Si, en cambio, está relacionado con algo así como printGhci que se implementó como:

class ShowGhci a where showGhci :: a -> String ... -- Bunch of instances? instance ShowGhci Char where ... -- The instance we want to be different. printGhci :: ShowGhci a => a -> IO () printGhci = putStrLn . showGhci

Ghci podría entonces cambiar lo que se imprime al poner diferentes instancias en contexto.


Ahora que sé ghci -interactive-print , esta es una gran característica. ¡Muchas gracias por escribir la pregunta y las respuestas! Por cierto, las impresoras bonitas existentes que puedo encontrar en la web tienen algunos casos de esquina , y el problema de escribir un buen show Unicode resultó ser más complicado de lo que parece.

Por lo tanto, decidí escribir un paquete Haskell unicode-show para este propósito, que (con suerte) imprime bien las cadenas de esquinas y los tipos compuestos .

Mis mejores deseos, que este paquete sea útil para las personas que buscaron este Q & A :)


Ha habido algún progreso con este tema; gracias a bravit (Vitaly Bragilevsky) !:

Probablemente incorporado en GHC 7.6.1. (¿Lo es?..)

Cómo hacer que se imprima cirílico ahora :

El parámetro pasado a GHCi debe ser una función que puede imprimir cirílico. No se ha encontrado tal función en Hackage. Entonces, tenemos que crear un contenedor simple, como por ahora:

module UPPrinter where import System.IO import Text.PrettyPrint.Leijen upprint a = (hPutDoc stdout . pretty) a >> putStrLn ""

Y ejecute ghci esta manera: ghci -interactive-print=UPPrinter.upprint UPPrinter

Por supuesto, esto se puede escribir de una vez por todas en .ghci .

Problema práctico: crear un agradable Show alternativo

Entonces, ahora hay un problema práctico: ¿qué usar como sustituto del Show estándar que escapa a los símbolos deseados?

Usar el trabajo de otros: otras lindas impresoras

Arriba, se sugiere Text.PrettyPrint.Leijen , probablemente porque se sabe que no escapan tales símbolos en cadenas.

Nuestro propio Show basado en Show - atractivo, pero no práctico

¿Qué tal escribir nuestro propio Show , por ejemplo, ShowGhci como se sugirió en una respuesta aquí? ¿Es práctico?

Para guardar el trabajo que define las instancias para una clase alternativa de Show (como ShowGhci ), uno podría sentirse tentado de usar las instancias existentes de Show por defecto, solo redefinir la instancia para String y Char . Pero eso no funcionará, porque si usas showGhci = show , entonces para cualquier información compleja que contenga cadenas show es "hard-compiled" para llamar a old show para mostrar la cadena. Esta situación requiere la capacidad de pasar diferentes diccionarios que implementan la misma interfaz de clase a funciones que usan esta interfaz ( show pasará a sub show s). Cualquier extensión GHC para esto?

Basarse en Show y querer redefinir solo las instancias de Char y String no es muy práctico, si desea que sea tan "universal" (ampliamente aplicable) como Show .

Programa de re-análisis

Una solución más práctica (y breve) se encuentra en otra respuesta aquí: analizar el resultado del show para detectar caracteres y cadenas, y volver a formatearlos. (Aunque parece un poco feo semánticamente, la solución es corta y segura en la mayoría de los casos (si no hay citas usadas para otros fines en el show ; no debe ser el caso para cosas estándar, porque la idea de show es ser más, o -a menos correcto Haskell analizable.)

Tipos semánticos en tus programas

Y un comentario más.

En realidad, si nos importa depurar en GHCi (y no simplemente demostrar Haskell y querer tener un resultado bonito), la necesidad de mostrar letras que no sean ASCII debe venir de alguna presencia inherente de estos caracteres en su programa (de lo contrario, para la depuración, puede sustituirlos por caracteres latinos o no les importa que se muestren los códigos). En otras palabras, hay algún SIGNIFICADO en estos caracteres o cadenas desde el punto de vista del dominio del problema. (Por ejemplo, recientemente me he involucrado en el análisis gramatical de ruso, y las palabras rusas como parte de un diccionario de ejemplo estaban "inherentemente" presentes en mi programa. Su trabajo solo tendría sentido con estas palabras específicas. Así que necesitaba léelos cuando se depura.)

Pero mire, si las cuerdas tienen algún significado, entonces ya no son cuerdas simples; son datos de un tipo significativo. Probablemente, el programa sería aún mejor y más seguro si declarara un tipo especial para este tipo de significados.

Y luego, ¡hurra !, simplemente defines tu instancia de Show para este tipo. Y está bien con la depuración de su programa en GHCi.

Como ejemplo, en mi programa de análisis gramatical, he hecho:

newtype Vocable = Vocable2 { ortho :: String } deriving (Eq,Ord) instance IsString Vocable -- to simplify typing the values (with OverloadedStrings) where fromString = Vocable2 . fromString

y

newtype Lexeme = Lexeme2 { lemma :: String } deriving (Eq,Ord) instance IsString Lexeme -- to simplify typing the values (with OverloadedStrings) where fromString = Lexeme2 . fromString

(el extra fromString aquí es porque podría cambiar la representación interna de String a ByteString o lo que sea)

Además de poder show bien, me sentí más seguro porque no podría mezclar diferentes tipos de palabras al componer mi código.


Las cosas cambiarán en la próxima versión 7.6.1 de Ghci, ya que proporciona una nueva opción de Ghci llamada: -interactive-print. Aquí está copiado de ghc-manual: (Y escribí myShow y myPrint de la siguiente manera)

2.4.8. Using a custom interactive printing function [New in version 7.6.1] By default, GHCi prints the result of expressions typed at the prompt using the function System.IO.print. Its type signature is Show a => a -> IO (), and it works by converting the value to String using show. This is not ideal in certain cases, like when the output is long, or contains strings with non-ascii characters. The -interactive-print flag allows to specify any function of type C a => a -> IO (), for some constraint C, as the function for printing evaluated expressions. The function can reside in any loaded module or any registered package. As an example, suppose we have following special printing module: module SpecPrinter where import System.IO sprint a = putStrLn $ show a ++ "!" The sprint function adds an exclamation mark at the end of any printed value. Running GHCi with the command: ghci -interactive-print=SpecPrinter.sprinter SpecPrinter will start an interactive session where values with be printed using sprint: *SpecPrinter> [1,2,3] [1,2,3]! *SpecPrinter> 42 42! A custom pretty printing function can be used, for example, to format tree-like and nested structures in a more readable way. The -interactive-print flag can also be used when running GHC in -e mode: % ghc -e "[1,2,3]" -interactive-print=SpecPrinter.sprint SpecPrinter [1,2,3]! module MyPrint (myPrint, myShow) where -- preparing for the 7.6.1 myPrint :: Show a => a -> IO () myPrint = putStrLn . myShow myShow :: Show a => a -> String myShow x = con (show x) where con :: String -> String con [] = [] con li@(x:xs) | x == ''/"'' = ''/"'':str++"/""++(con rest) | x == ''/''' = ''/''':char:''/''':(con rest'') | otherwise = x:con xs where (str,rest):_ = reads li (char,rest''):_ = reads li

Y funcionan bien:

*MyPrint> myPrint "asf萨芬速读法" "asf萨芬速读法" *MyPrint> myPrint "asdffasdfd" "asdffasdfd" *MyPrint> myPrint "asdffa撒旦发" "asdffa撒旦发" *MyPrint> myPrint ''此'' ''此'' *MyPrint> myShow ''此'' "''/27492''" *MyPrint> myPrint ''此'' ''此''


Lo que sería ideal es un parche para ghci que le permita al usuario :set una función para mostrar resultados que no sean show . No existe tal característica actualmente. Sin embargo, la sugerencia de Don para una macro a :def (con o sin el paquete de texto) no está nada mal.


Podría cambiar a usar el paquete ''text'' para IO. P.ej

Prelude> :set -XOverloadedStrings Prelude> Data.Text.IO.putStrLn "hello: привет" hello: привет

El paquete forma parte de la distribución estándar de Haskell, la plataforma Haskell , y proporciona un tipo de texto Unicode inmutable, empaquetado y eficiente con operaciones IO. Muchas codificaciones son compatibles .

Usando un archivo .ghci, puede configurar -XOverloadStrings para que esté activado por defecto, y escribir una macro :def para introducir un comando :text que muestre un valor solo a través del text . Eso funcionaria.


Una forma de hackear esto es envolver GHCi en un contenedor de shell que lea sus caracteres Unicode extendidos y no seleccionados. Esta no es la forma de Haskell, por supuesto, pero cumple su función :)

Por ejemplo, esto es un contenedor ghci-esc que usa sh y python3 (3 es importante aquí):

#!/bin/sh ghci "$@" | python3 -c '' import sys import re def tr(match): s = match.group(1) try: return chr(int(s)) except ValueError: return s for line in sys.stdin: sys.stdout.write(re.sub(r"//([0-9]{4})", tr, line)) ''

Uso de ghci-esc :

$ ./ghci-esc GHCi, version 7.0.2: http://www.haskell.org/ghc/ :? for help > "hello" "hello" > "привет" "привет" > ''Я'' ''Я'' > show ''Я'' "''/Я''" > :q Leaving GHCi.

Tenga en cuenta que no todo lo anterior se hace correctamente, pero esta es una manera rápida de mostrar la salida de Unicode a su audiencia.