It's a little bit of overkill, but one solution is to combine OverloadedStrings
and RebindableSyntax
. The RebindableSyntax
extension causes all the implicit function calls that Haskell syntax uses to refer to whatever functions are in scope; for instance, integer literals use any fromIntegral
, not necessarily Prelude.fromIntegral
. As a side effect, Prelude
is no longer implicitly imported, so you have to do that manually. As long as you do import it, there shouldn't be any issues with syntax using the wrong function implicitly (I think—I haven't actually used this technique). When combined with OverloadedStrings
, this causes "foo"
to be transformed into fromString "foo"
for whatever fromString
's in scope, not necessarily Data.String.fromString "foo"
. So making fromString
synonymous with pack
will do what you want. A complete example:
{-# 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!"
This works fine, and changing main
to main = putStrLn "Hello, world!"
produces the desired error:
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!"
Commenting out the definition of fromString
causes a different error:
TestStrings.hs:11:19:
Not in scope: `fromString'
Perhaps you meant `showString' (imported from Prelude)
If you want it to work with both strict and lazy text, you could define your own IsString
type class, and make both of them instances; the class doesn't have to be called IsString
, just so long as it has a fromString
method.
Also, a word of warning: the section of the GHC manual on RebindableSyntax
doesn't mention the fromString
function, and the section on OverloadedStrings
doesn't mention RebindableSyntax
. There's no reason this shouldn't work, but I think that means that this solution technically relies on undocumented behavior.
asText :: Text -> Text; asText = id
is not an acceptable solution. – Sardine