考虑以下示例(?>= ghci,$= shell):
?> writeFile "d" $ show "d"
$ cat d
"d"
?> writeFile "d" "d"
$ cat d
d
?> writeFile "backslash" $ show "\\"
$ cat backslash
"\\"
?> writeFile "backslash" "\\"
$ cat backslash
\
?> writeFile "cat" $ show "" -- U+1F408
$ cat cat
"\128008"
?> writeFile "cat" ""
$ cat cat
Run Code Online (Sandbox Code Playgroud)
我理解另一种方式"\128008"只是""在Haskell源代码中表示的另一种方式
.我的问题是:为什么""示例的行为类似于反斜杠而不是像"d"?既然它是一个可打印的角色,它不应该像一个字母吗?
更一般地说,确定字符是显示为可打印字符还是转义码的规则是什么?我查看 了Haskell 2010语言报告中的第6.3节,但没有指定确切的行为.
shown作为图形字符.*其他所有内容都将被转义.*除了双引号(因为它们用于字符串分隔符)和反斜杠(因为它们需要用于转义).
让我们看一下源代码来解决这个问题吧!
既然我们有String = [Char],我们应该instance Show Char在源头寻找.它可以在这里找到
.它被定义为:
-- | @since 2.01
instance Show Char where
showsPrec _ '\'' = showString "'\\''"
showsPrec _ c = showChar '\'' . showLitChar c . showChar '\''
showList cs = showChar '"' . showLitString cs . showChar '"'
Run Code Online (Sandbox Code Playgroud)
所以显示一个String(使用showList)基本上是一个包装器
ShowLitString,并显示一个Char是一个包装器ShowLitChar.我们来看看这些功能.
showLitString :: String -> ShowS
-- | Same as 'showLitChar', but for strings
-- It converts the string to a string using Haskell escape conventions
-- for non-printable characters. Does not add double-quotes around the
-- whole thing; the caller should do that.
-- The main difference from showLitChar (apart from the fact that the
-- argument is a string not a list) is that we must escape double-quotes
showLitString [] s = s
showLitString ('"' : cs) s = showString "\\\"" (showLitString cs s)
showLitString (c : cs) s = showLitChar c (showLitString cs s)
-- [explanatory comments ...]
Run Code Online (Sandbox Code Playgroud)
正如您可能预期的那样,showLitString主要是一个包装器
showLitChar.[注意:如果你不熟悉这个ShowS类型,这是一个很好的
答案,可以理解为什么它可能有用.]不是我们想要的,所以让我们去showLitChar(我省略了定义的部分内容)与问题相关).
-- | Convert a character to a string using only printable characters,
-- using Haskell source-language escape conventions. For example:
-- [...]
showLitChar :: Char -> ShowS
showLitChar c s | c > '\DEL' = showChar '\\' (protectEsc isDec (shows (ord c)) s)
-- ^ Pattern matched for cat
showLitChar '\DEL' s = showString "\\DEL" s
showLitChar '\\' s = showString "\\\\" s
-- ^ Pattern matched for backslash
showLitChar c s | c >= ' ' = showChar c s
-- ^ Pattern matched for d
-- Some more escape codes
showLitChar '\a' s = showString "\\a" s
-- similarly for '\b', '\f', '\n', '\r', '\t', '\v' etc.
-- showLitChar ... = ...
Run Code Online (Sandbox Code Playgroud)
现在你看到问题所在.ord c是一个int,第一个是所有非ASCII字符(ord '\DEL' == 127).对于ASCII范围内的字符,将打印可打印字符,其余字符将被转义.对于外面的字符,所有字符都被转义.
代码没有回答问题的"为什么"部分.答案(我认为)是我们看到的第一个评论:
-- | @since 2.01
instance Show Char where
Run Code Online (Sandbox Code Playgroud)
如果我在猜测,这种行为一直存在以保持向后兼容性.我不需要猜测:请参阅评论以获得一些好的答案.
我们可以git blame使用GHC的Github镜像在线进行;).让我们看看这段代码的编写时间(责备链接).相关提交是15岁(!).但是,确实提到了Unicode.
Data.Char模块中存在区分不同类型的Unicode字符的功能.看源头:
isPrint c = iswprint (ord c) /= 0
foreign import ccall unsafe "u_iswprint"
iswprint :: Int -> Int
Run Code Online (Sandbox Code Playgroud)
如果你跟踪引入的提交iswprint,你将在这里登陆
.这项承诺是在13年前作出的.
也许在这两年里写的代码足够他们不想破解?我不知道.如果一些GHC开发人员能够更多地了解这一点,那就太棒了:).Daniel Wagner和Paul Johnson在评论中指出了一个很好的理由 - 使用非Unicode系统操作必须是高优先级(大约15年前),因为当时Unicode相对较新.