2012-08-27 7 views
6

En la mayoría de los lenguajes de OO con los que estoy familiarizado, el método toString de String es en realidad solo la función de identidad. Pero en Haskell show agrega comillas dobles.¿Hay una función polimórfica `toString` que no agregue comillas?

Así que si escribo una función algo como esto

f :: Show a => [a] -> String 
f = concat . map show 

funciona como se espera para los números

f [0,1,2,3] -- "0123" 

pero Cuerdas terminan con citas adicionales

f ["one", "two", "three"] -- "\"one\"\"two\"\"three\"" 

cuando realmente quiere "onetwothree".

Si quería escribir f polimórfica, hay una manera de hacerlo con sólo una restricción Show, y sin anular la instancia Show para cuerdas (si eso es posible).

Lo mejor que puedo llegar a es crear mi propia clase de tipo:

class (Show a) => ToString a where 
    toString = show 

y añadir una instancia para todo?

instance ToString String where toString = id 
instance ToString Char where toString = pure 
instance ToString Int 
instance ToString Maybe 
...etc 
+0

Terminé usando el constructo 'newtype' para crear un [' LiteralString'] (https://github.com/corsis/PortFusion/blob/ad63a006cff324667cca2316699e26a0078fbc02/src/Main.hs#L67) tipo con 'Mostrar personalizado 'y' Lectura' instancias: https://github.com/corsis/PortFusion/blob/ad63a006cff324667cca2316699e26a0078fbc02/src/Main.hs#L67 –

+0

Vea también http://stackoverflow.com/questions/12102874/haskell-suppress-quotes -around-strings-when-shown – sth

+0

Tenga en cuenta que 'Show' no solo agrega comillas dobles. También se escapa de caracteres como saltos de línea. Por ejemplo, la cadena de un carácter '" \ n "' se muestra como una cadena de cuatro caracteres, con los caracteres ", \, n,". – sdcvvc

Respuesta

3

Usted puede hacer esto:

{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-} 

class Show a => ToString a where 
    toString :: a -> String 

instance Show a => ToString a where 
    toString = show 

instance ToString String where 
    toString = id 

Prelude> toString "hello" 
"hello" 
Prelude> toString 3 
"3" 

Tenga en cuenta que esto es probablemente una idea terrible.

+0

Gracias. Disfruté especialmente sus últimas palabras "Tenga en cuenta que esta es probablemente una idea terrible". :) ¿Puedes explicar qué hacen las diferentes extensiones de idioma para habilitar esto? –

+3

Sin 'FlexibleInstances', no está permitido que las variables de tipo desnudo sean miembros de clases de tipos. Sin "IndefinidasInstancias", al menos uno de los argumentos de tipo de la instancia no debe ser una variable de tipo desnudo. No tener ninguno de estos no permitiría la declaración de la primera instancia anterior. Y sin 'OverlappingInstances' no podríamos tener las dos instancias anteriores juntas, porque obviamente la primera instancia se aplica a todos los tipos visibles, y' String' es de ese tipo, es decir, se superponen. –

+0

¿Si tu comentario acerca de que (probablemente) sea una idea terrible, es el problema de que las instancias puedan filtrarse a otros módulos? –

2

Usted podría utilizar newtype con OverloadedStrings:

{-# LANGUAGE OverloadedStrings #-} 

import   Data.ByteString.Char8  (ByteString) 
import qualified Data.ByteString.Char8 as B 

newtype LiteralString = LS ByteString 
instance IsString LiteralString where fromString = LS . B.pack 
instance Show  LiteralString where show (LS x) = B.unpack x 
instance Read  LiteralString where readsPrec p s = map (\(!s, !r) -> (LS s,r)) $! readsPrec p s 

hello :: LiteralString 
hello = "hello world" 

main :: IO() 
main = putStrLn . show $! hello 

de salida:

hello world 

Las comillas dobles en el caso normal son realmente útiles cuando se lee una cadena se muestra de nuevo en el contexto de expresión más grande, ya que delimitan claramente los valores de cadena mostrados a partir de los valores de otros tipos que han demostrado:

x :: (ByteString, Int) 
x =  read . show $! ("go", 10) 
-- string value starts --^^-- ends 

y :: (LiteralString, Int) 
y =  read . show $! ("go", 10) 
-- string value starts --^  ^consumes all characters; read fails 
+0

Creo que el principal problema es que todavía depende de que la persona que llama sepa que debe conocer tienen que usar un tipo de Cadena no estándar. –

+3

Creo que el principal problema con 'show' es su fuerte acoplamiento con' lectura' sin importar lo que las personas digan en contra de usarlos para fines de impresión/análisis; comportamiento predeterminado de 'leer. show = id' y esa parece ser la razón de las comillas dobles. –

5

La clase Pretty y su correspondiente tipo Doc tienen el comportamiento necesario para Mostrar. Su enlace muestra un caso de uso diferente, sin embargo; tal vez podrías editar la pregunta?

+0

Gracias, no sabía sobre esta clase. –

7

Creo que la causa de su problema es que show no es realmente renderToText. Se supone que debe producir texto que pueda pegar en el código Haskell para obtener el mismo valor, o convertir de nuevo al mismo valor usando read.

Para este propósito, show "foo" = "foo" no funcionaría, porque show "1" = "1" y show 1 = "1", que pierde información.

La operación que desea ser capaz de aplicar a "foo" para obtener "foo" y para 1 para obtener "1" es algo distinto de show. show simplemente no es un Java-esque toString.

Cuando lo he necesitado antes, de hecho he hecho mi propia clase de tipos y he hecho muchas instancias de ella, y luego la he usado en vez de Show. La mayoría de las instancias se implementaron con show, pero String no fue el único que quería personalizar, por lo que la clase de tipo separada no se desperdició por completo. En la práctica, descubrí que solo había un puñado de tipos de los que realmente necesitaba la instancia, y era bastante trivial agregarlos ya que obtuve errores de compilación.

+0

Ben, su respuesta no es precisa. El objetivo de 'Show' es representar un valor de Haskell en una cadena. No hay obligación de tener instancias isomorfas de Lectura/Show - ver el Informe Haskell. Es cierto que las instancias de Read/Show isomórficas son útiles, pero no son obligatorias. Si desea serialización, considere usar 'Binary' en su lugar. –

+1

Bueno, eso es cierto, pero dado que las instancias de biblioteca estándar (y las producidas por 'derivando') usan sintaxis Haskell, está bastante claro que la audiencia prevista para las cadenas producidas por' show' son los programadores Haskell. No está destinado a "formatear este texto de valor para que pueda mostrarlo a los usuarios". – Ben

Cuestiones relacionadas