Make rebindable fail
work with overloaded strings¶
We propose that string literals generated by GHC and passed to user
defined functions should have fromString
applied. The only
instance of such GHC generated strings we are aware of is via
fail
, so we propose to fix that case. Concretely, for monadic
pattern match failures in the presence of the extensions
RebindableSyntax
and OverloadedStrings
, we desugar to fail
(fromString "error")
instead of fail "error"
.
Motivation¶
We might expect the following program to work:
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE OverloadedStrings #-}
module Fail where
import Prelude hiding (fail)
foo x = do
Just y <- x
return y
newtype Text = Text String
fail :: Text -> a
fail (Text x) = error x
fromString :: String -> Text
fromString = Text
The intent is that the defined fail
should take an argument of
type Text
which is obtained by implicit conversion from
String
.
However, it fails with,
Fail.hs:8:5-15: error:
* Couldn't match expected type `[Char]' with actual type `Text'
* In a stmt of a 'do' block: Just y <- x
In the expression:
do Just y <- x
return y
In an equation for `foo':
foo x
= do Just y <- x
return y
|
8 | Just y <- x
| ^^^^^^^^^^^
Proposed Change Specification¶
We propose that monadic pattern match failure in the presence of
RebindableSyntax
and OverloadedStrings
desugars to fail
(fromString "error")
instead of fail "error"
.
Effects and Interactions¶
With this modification, the example program from the “Motivation”
section will type-check. It would be possible for a
Prelude
-replacement to eliminate String
more completely,
without having to retain it as the input type to fail
.
We view this change primarily as a bug fix, which simplifies an unexpected corner. This proposal originally started out as https://gitlab.haskell.org/ghc/ghc/issues/15645.
Costs and Drawbacks¶
There may be a small number of libraries that fail to type-check, but
any that have simply enabled the two extensions will continue to work,
as fromString
has an instance for String
. In all likelihood,
these are the libraries that have already tried to eliminate
String
, so are most likely to benefit from the change.
Alternatives¶
One can work around the problem by requiring the user to write fail
= myFail . fromString
, but that requires both fail
and
myFail
to be in scope, and the name fail
cannot be reused for
the failure function in the monad typeclass.
Unresolved Questions¶
Should fromString
be injected only when both OverloadedStrings
and RebindableSyntax
are enabled or just OverloadedStrings
? We
suggest only when both as, when RebindableSyntax
is not enabled,
the operation fail . fromString
is equivalent to fail
.
Implementation Plan¶
We (the proposal authors) will implement the change. Our plan is to
modify getFailFunction
and failFunction
in RnExpr.hs
so
that if the RebindableSyntax
and OverloadedStrings
language
extensions are enabled, then fail_op
becomes fail . fromString
rather than just fail
.