是否存在不添加引号的多态`toString`函数?

时间:2012-08-27 23:59:19

标签: string haskell polymorphism

在我熟悉的大多数OO语言中,toString的{​​{1}}方法实际上只是身份函数。但在Haskell中,String添加了双引号。

所以如果我写一个像这样的函数

show

它按预期的数字

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

但是Strings最终会有额外的引号

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

当我真的想要f ["one", "two", "three"] -- "\"one\"\"two\"\"three\""

如果我想以多态方式编写"onetwothree",是否有办法只使用f约束,并且不覆盖Show的String实例(如果可能的话)。

我能想到的最好的方法是创建自己的类型类:

Show

并为所有内容添加实例?

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

4 个答案:

答案 0 :(得分:8)

我认为问题的根本原因是show并非renderToText。它应该生成可以粘贴到Haskell代码中的文本以获取相同的值,或者使用read转换回相同的值。

为此目的,show "foo" = "foo"无效,因为show "1" = "1"show 1 = "1"会丢失信息。

您希望能够应用于"foo"以获取"foo"以及1获取"1"的操作不是showshow只是不是Java风格的toString

当我之前需要这个时,我确实创建了自己的新类型,并制作了一堆实例,然后使用它而不是Show。大多数实例都使用show实现,但String并不是我想要自定义的唯一实例,因此单独的类型类并未完全浪费。在实践中,我发现实际上只需要少量类型的实例,并且在编译错误时添加它们非常简单。

答案 1 :(得分:5)

Pretty类及其对应的类型Doc具有Show所需的行为。但是,您的链接显示了不同的用例;也许你可以编辑这个问题?

答案 2 :(得分:3)

你可以这样做:

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

请注意,这可能是一个糟糕的主意。

答案 3 :(得分:2)

您可以将newtypeOverloadedStrings

一起使用
{-# 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

输出:

hello world

正常情况下的双引号在更大表达式的上下文中读取显示的字符串时实际上很有用,因为它们清楚地界定了显示的其他类型的值的字符串值:

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