Modify IsString String instance (fixes #10814)
authorDan Doel <>
Mon, 21 Dec 2015 11:28:16 +0000 (12:28 +0100)
committerBen Gamari <>
Mon, 21 Dec 2015 11:28:52 +0000 (12:28 +0100)
The new instance resolves to `s ~ [Char]` as soon as we know that `s ~
[a]`, to avoid certain functions (like (++)) causing a situation where
`a` is ambiguous and (currently) unable to be defaulted.

Reviewers: #core_libraries_committee, hvr, austin, bgamari

Reviewed By: hvr, bgamari

Subscribers: thomie

Differential Revision:

GHC Trac Issues: #10814

testsuite/tests/overloadedstrings/Makefile [new file with mode: 0644]
testsuite/tests/overloadedstrings/should_run/Makefile [new file with mode: 0644]
testsuite/tests/overloadedstrings/should_run/all.T [new file with mode: 0644]
testsuite/tests/overloadedstrings/should_run/overloadedstringsrun01.hs [new file with mode: 0644]
testsuite/tests/overloadedstrings/should_run/overloadedstringsrun01.stdout [new file with mode: 0644]

index a03569f..df410f0 100644 (file)
@@ -1,5 +1,6 @@
 {-# LANGUAGE Trustworthy #-}
 {-# LANGUAGE NoImplicitPrelude, FlexibleInstances #-}
+{-# LANGUAGE TypeFamilies #-}
 -- |
@@ -34,6 +35,45 @@ import Data.List (lines, words, unlines, unwords)
 class IsString a where
     fromString :: String -> a
-instance IsString [Char] where
+Note [IsString String]
+Previously, the IsString instance that covered String was a flexible
+instance for [Char]. This is in some sense the most accurate choice,
+but there are cases where it can lead to an ambiguity, for instance:
+  show $ "foo" ++ "bar"
+The use of (++) ensures that "foo" and "bar" must have type [t] for
+some t, but a flexible instance for [Char] will _only_ match if
+something further determines t to be Char, and nothing in the above
+example actually does.
+So, the above example generates an error about the ambiguity of t,
+and what's worse, the above behavior can be generated by simply
+   "foo" ++ "bar"
+into GHCi with the OverloadedStrings extension enabled.
+The new instance fixes this by defining an instance that matches all
+[a], and forces a to be Char. This instance, of course, overlaps
+with things that the [Char] flexible instance doesn't, but this was
+judged to be an acceptable cost, for the gain of providing a less
+confusing experience for people experimenting with overloaded strings.
+It may be possible to fix this via (extended) defaulting. Currently,
+the rules are not able to default t to Char in the above example. If
+a more flexible system that enabled this defaulting were put in place,
+then it would probably make sense to revert to the flexible [Char]
+instance, since extended defaulting is enabled in GHCi. However, it
+is not clear at the time of this note exactly what such a system
+would be, and it certainly hasn't been implemented.
+A test case (should_run/overloadedstringsrun01.hs) has been added to
+ensure the good behavior of the above example remains in the future.
+instance (a ~ Char) => IsString [a] where
     fromString xs = xs
index a86a176..82def76 100644 (file)
   * New `GHC.Generics.packageName` operation
-  * New `GHC.Stack.Types` module now contains the definition of
-    `CallStack` and `SrcLoc`
-  * New `GHC.Stack.Types.pushCallStack` function pushes a call-site onto a `CallStack`
-  * `GHC.SrcLoc` has been removed
-  * `GHC.Stack.showCallStack` and `GHC.SrcLoc.showSrcLoc` are now called
-    `GHC.Stack.prettyCallStack` and `GHC.Stack.prettySrcLoc` respectively
+  * New `GHC.Stack.CallStack` data type
   * `Complex` now has `Generic`, `Generic1`, `Functor`, `Foldable`, `Traversable`,
     `Applicative`, and `Monad` instances
   * Redesign `GHC.Generics` to use type-level literals to represent the
     metadata of generic representation types (#9766)
+  * The `IsString` instance for `[Char]` has been modified to eliminate
+    ambiguity arising from overloaded strings and functions like `(++)`.
 ##  *Oct 2015*
   * Bundled with GHC 7.10.3
diff --git a/testsuite/tests/overloadedstrings/Makefile b/testsuite/tests/overloadedstrings/Makefile
new file mode 100644 (file)
index 0000000..9a36a1c
--- /dev/null
@@ -0,0 +1,3 @@
+include $(TOP)/mk/
+include $(TOP)/mk/
diff --git a/testsuite/tests/overloadedstrings/should_run/Makefile b/testsuite/tests/overloadedstrings/should_run/Makefile
new file mode 100644 (file)
index 0000000..9101fbd
--- /dev/null
@@ -0,0 +1,3 @@
+include $(TOP)/mk/
+include $(TOP)/mk/
diff --git a/testsuite/tests/overloadedstrings/should_run/all.T b/testsuite/tests/overloadedstrings/should_run/all.T
new file mode 100644 (file)
index 0000000..8248302
--- /dev/null
@@ -0,0 +1 @@
+test('overloadedstringsrun01', normal, compile_and_run, [''])
diff --git a/testsuite/tests/overloadedstrings/should_run/overloadedstringsrun01.hs b/testsuite/tests/overloadedstrings/should_run/overloadedstringsrun01.hs
new file mode 100644 (file)
index 0000000..87b4303
--- /dev/null
@@ -0,0 +1,3 @@
+{-# LANGUAGE OverloadedStrings #-}
+main = print $ "hello" ++ " world."
diff --git a/testsuite/tests/overloadedstrings/should_run/overloadedstringsrun01.stdout b/testsuite/tests/overloadedstrings/should_run/overloadedstringsrun01.stdout
new file mode 100644 (file)
index 0000000..ff3e71d
--- /dev/null
@@ -0,0 +1 @@
+"hello world."