2012-08-27 1 views
6

In den meisten OO-Sprachen, die ich kenne, ist die toString Methode von String eigentlich nur die Identitätsfunktion. Aber in Haskell show fügt doppelte Anführungszeichen hinzu.Gibt es eine polymorphe toString-Funktion, die keine Anführungszeichen hinzufügt?

Also, wenn ich eine Funktion schreiben, so etwas wie dies

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

es für Zahlen

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

aber Strings am Ende mit zusätzlichen Anführungszeichen wie erwartet funktioniert

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

wenn ich wirklich wollen "onetwothree".

Wenn ich polymorph schreiben wollte f, gibt es eine Möglichkeit, es mit nur einer Show Einschränkung zu tun, und ohne die Show-Instanz für String überschreiben (wenn das überhaupt möglich ist).

Das Beste, was ich mit oben kommen kann, ist meine eigene Art Klasse zu erstellen:

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

und eine Instanz für alles hinzufügen?

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

Ich endete mit dem 'newtype' Konstrukt, um einen [' LiteralString'] (https://github.com/corsis/PortFusion/blob/ad63a006cff324667cca2316699e26a0078fbc02/src/Main.hs#L67) Typ mit benutzerdefinierten 'Show 'und' Read'-Instanzen: https://github.com/corsis/PortFusion/blob/ad63a006cff324667cca2316699e26a0078fbc02/src/Main.hs#L67 –

+0

Siehe auch http://stackoverflow.com/questions/12102874/haskell-suppress-quotes -around-strings-when-show – sth

+0

Beachten Sie, dass 'Show' nicht nur doppelte Anführungszeichen enthält. Es entgeht auch Zeichen wie Zeilenumbrüche. Zum Beispiel wird die Ein-Zeichen-Zeichenkette "\ n" 'als eine Vier-Zeichen-Zeichenkette mit den Zeichen", \, n, "angezeigt. – sdcvvc

Antwort

3

Sie können dies tun:

{-# 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" 

Hinweis, dass dies wahrscheinlich eine schreckliche Idee.

+0

Danke. Ich habe besonders Ihre letzten Worte genossen. "Beachten Sie, dass dies wahrscheinlich eine schreckliche Idee ist." :) Können Sie erklären, was die verschiedenen Spracherweiterungen tatsächlich tun, um dies zu ermöglichen? –

+3

Ohne 'FlexibleInstances' dürfen Sie keine Variablen vom Typ blank als Member von Typklassen haben. Ohne 'UndecidableInstances' darf mindestens eines der Argumentargumente der Instanz keine Variable vom Typ bare sein. Wenn Sie keines von beiden haben, würde die obige Deklaration nicht zugelassen. Und ohne "OverlappingInstances" könnten wir beide der obigen Instanzen nicht zusammen haben, da offensichtlich die erste Instanz für alle darstellbaren Typen gilt, und "String" ist ein solcher Typ, d.h. sie überlappen sich. –

+0

Wenn Ihr Kommentar darüber (wahrscheinlich) eine schreckliche Idee ist, ist das Problem, dass die Instanzen in andere Module eindringen könnten? –

2

könnten Sie verwenden newtype mit 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 

Ausgang:

hello world 

Die doppelten Anführungszeichen im Normalfall sind tatsächlich sinnvoll, wenn eine dargestellte Zeichenfolge zurück in dem Lese der Kontext größerer Ausdrücke, da sie die angezeigten Zeichenfolgenwerte eindeutig von Werten abgrenzen von anderen gezeigt Typen:

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

Ich denke, das Hauptproblem dabei ist, dass es immer noch auf den Anrufer der Funktion angewiesen ist zu wissen Sie müssen einen nicht standardmäßigen String-Typ verwenden. –

+3

Ich denke, das Hauptproblem mit 'show' ist die starke Kopplung mit' read', egal was die Leute sagen, wenn man sie für hübsche Druck-/Parsing-Zwecke benutzt; Standardverhalten von 'lesen. show = id' und das scheint der Grund für doppelte Anführungszeichen zu sein. –

5

Die Pretty Klasse und ihrem entsprechenden Typ Doc haben das erforderliche Verhalten für Show. Ihr Link zeigt jedoch einen anderen Anwendungsfall; vielleicht könnten Sie die Frage bearbeiten?

+0

Danke, ich wusste nichts über diese Klasse. –

7

Ich denke, die Ursache für Ihr Problem ist, dass show ist nicht wirklich renderToText. Es soll Text erzeugen, den Sie in Haskell-Code einfügen können, um den gleichen Wert zu erhalten, oder mit read wieder in denselben Wert konvertieren.

Für diesen Zweck würde show "foo" = "foo" nicht funktionieren, weil und show 1 = "1", die Informationen verliert.

Der Vorgang, den Sie in der Lage sein wollen "foo" anzuwenden "foo" zu erhalten und zu 1"1" zu bekommen, ist etwas anderes als show. show ist einfach kein Java-esque toString.

Wenn ich das vorher benötigt habe, habe ich tatsächlich meine eigene neue Typklasse gemacht und eine Menge Dinge davon gemacht, und dann diese anstelle von Show verwendet. Die meisten Instanzen wurden mit show implementiert, aber String war nicht der einzige, den ich anpassen wollte, so dass die separate Typklasse nicht vollständig verschwendet wurde. In der Praxis stellte ich fest, dass es nur eine Handvoll Typen gab, für die ich die Instanz tatsächlich benötigte, und es war ziemlich trivial, sie hinzuzufügen, da ich Kompilierfehler bekam.

+0

Ben, Ihre Antwort ist nicht korrekt. Der Zweck von 'Show' ist es, einem String einen Haskell-Wert zu geben. Es besteht keine Verpflichtung, isomorphe Read/Show-Instanzen zu haben - siehe Haskell Report. Zugegeben, isomorphe Read/Show-Instanzen sind nützlich, aber sie sind nicht obligatorisch. Wenn Sie eine Serialisierung wünschen, sollten Sie stattdessen 'Binary' verwenden. –

+1

Nun, das stimmt, aber da die Standardbibliotheksinstanzen (und die von 'derivating' erzeugten) alle die Haskell-Syntax verwenden, ist es ziemlich klar, dass die beabsichtigte Zielgruppe für' show' produzierte Strings Haskell-Programmierer sind. Es ist nicht dafür gedacht, "diesen Werttext zu formatieren, damit ich ihn den Benutzern anzeigen kann". – Ben