限制字符串字面量仅为文本

43
我了解到OverloadedStrings语言扩展会在所有字符串字面量周围添加一个隐式的fromString。我想做的不是真正的重载字符串,而只是改变它们的意义,使它们总是转换为Text,因此,将字符串字面量用作字符列表应该导致类型错误。
似乎不可能仅导入IsString类而不导入该类的String实例。 GHC是否提供一些方法来限制字符串字面量仅限于Text

需要明确的是:手动将所有字符串字面量包装在函数 asText :: Text -> Text; asText = id 中并不是一个可接受的解决方案。 - Dan Burton
4
请注意,除非您具有异常巨大的文字字面量,否则Text可能并不比String更有效率。当您从IO获取字符串时,Text真正发挥作用。 - stephen tetley
请查看这个 haskell-cafe 线程 - Michael Steele
2个回答

51

有点过度杀伤力,但一个解决方法是结合OverloadedStringsRebindableSyntaxRebindableSyntax扩展会导致Haskell语法中的所有隐式函数调用引用当前作用域中的任何函数;例如,整数字面量使用任何fromIntegral,而不一定是Prelude.fromIntegral。副作用是Prelude不再隐式导入,所以你必须手动导入它。只要你确实导入了它,使用错误的函数进行语法上的隐式转换就不应该有任何问题(我觉得—我实际上没有使用过这种技术)。当与OverloadedStrings结合使用时,"foo"会被转换为fromString "foo",其中fromString是当前作用域中的任何fromString,而不一定是Data.String.fromString "foo"。因此,将fromStringpack同义化将实现你想要的效果。一个完整的示例:

{-# LANGUAGE OverloadedStrings, RebindableSyntax #-}
import Prelude

import qualified Data.Text    as T
import qualified Data.Text.IO as T

fromString :: String -> T.Text
fromString = T.pack

main :: IO ()
main = T.putStrLn "Hello, world!"

这个工作很好,将 main 改为 main = putStrLn "Hello, world!" 会产生所期望的错误。
TestStrings.hs:11:17:
    Couldn't match expected type `String' with actual type `T.Text'
    Expected type: [Char] -> String
      Actual type: String -> T.Text
    In the first argument of `putStrLn', namely `"Hello, world!"'
    In the expression: putStrLn "Hello, world!"

注释掉fromString的定义会导致不同的错误:
TestStrings.hs:11:19:
    Not in scope: `fromString'
    Perhaps you meant `showString' (imported from Prelude)

如果你想让它同时适用于严格和懒惰文本,你可以定义自己的IsString类型类,并使它们都成为实例;这个类不一定要叫做IsString,只要它有一个fromString方法就可以。
另外,需要提醒一下:GHC手册中关于RebindableSyntax的部分没有提到fromString函数,而关于OverloadedStrings的部分也没有提到RebindableSyntax。虽然理论上来说这个解决方案应该是可行的,但我认为这意味着这个解决方案在技术上依赖于未记录的行为。

5
有趣,我没有想到那一点。这确实有点过头了,但我这样做的原因也是同样程度的过头,所以一切都好。 - Dan Burton
3
T.pack 是在编译时还是运行时发生的? - Cetin Sert
10
核心代码(使用-O或-O2优化)是main2 = Data.Text.unpackCString# "Hello, world!",因此看起来它在运行时至少不会经过String - FunctorSalad
缺失的文档是一个漏洞,随时可以改进文档! - Joachim Breitner

1
现在还没有实现这种方式的方法,但也许最终可以通过正在讨论的GHC特性提案中提出的instance force来实现,其中你需要这样说。
instance force IsString Text

在你的模块中。这是该提案的主要激励示例之一。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接