Skip to content

Instantly share code, notes, and snippets.

@Wizek
Last active May 26, 2018 20:13
Show Gist options
  • Save Wizek/396b0a608fa93d7d458a78dbf7c88870 to your computer and use it in GitHub Desktop.
Save Wizek/396b0a608fa93d7d458a78dbf7c88870 to your computer and use it in GitHub Desktop.

Try commenting out line 21 in ./test-suite/HsDiExample/MainSpec.hs

21:      & override "logger"         [qc| \a -> modifyIORef logs (++ [a]) |]

Run it with stack test, and you'll get an error like the following:

• Ambiguous type variable ‘m0’
  prevents the constraint ‘(Monad m0)’ from being solved.
• When checking that the inferred type
    logger :: forall b t (m :: * -> *).
              Monad m =>
              Data.Text.Internal.Text -> IO ()
  is as general as its inferred signature
    logger :: Data.Text.Internal.Text -> IO ()

But! This is not a true type error in the sense that we like; that it would catch an invalid program that otherwise would fail with a run-time error. It's not, because deferring the error (with -fdefer-type-errors) causes the program (and test executable) to compile, link and execute without a problem!

The assertions do fail, but that's to be expected when we disable a mock, so it's beside the point. The point is that we don't run into the deferred typeerror exception.

So, it would seem to me that this type error is complaining about a vagueness in a portion of a piece of dead code that could never execute.

And perhaps if we could write default (IO) or default (Identity) then we could satisfy the compiler. Similar to how default (Text) appeases it when we have an unused binding of foo = "bar" with -XOverloadedStrings.

At least, that's as much as I understand of it at the moment. (1) I'd love for anyone to tell me more about what is as general as its inferred signature type errors supposed to mean. And/or (2) please show me a counterexample where the deferred-exception-producing part of the code can be reached. And/or, perhaps most appreciatedly, (3) please tell me what change could I make to hs-di-example or even hs-di that could alleviate having to defer type errors for the sake of the compiler every now and then.

Edit 2018-05-26:

Some further relevant detains can be found over on this StackOverflow question.

@mrkgnao
Copy link

mrkgnao commented Oct 28, 2017

This is probably similar to the "show . read" problem, which can reliably be made to trigger an error in case defaulting is disabled. Here, again, we have a type variable that isn't present in the type of the complete expression:

show . read :: (Show a, Read a) => String -> String

Here's an example in GHCi.

Prelude
<λ> :set -XNoExtendedDefaultRules

Prelude
<λ> :t show (read "5")
<interactive>:1:1: error:
    • Ambiguous type variable ‘a0’ arising from a use of ‘show’
      prevents the constraint ‘(Show a0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘a0’ should be.
      These potential instances exist:
        instance Show Ordering -- Defined in ‘GHC.Show’
        instance Show Integer -- Defined in ‘GHC.Show’
        instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
        ...plus 22 others
        ...plus 17 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the expression: show (read "5")
<interactive>:1:7: error:
    • Ambiguous type variable ‘a0’ arising from a use of ‘read’
      prevents the constraint ‘(Read a0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘a0’ should be.
      These potential instances exist:
        instance Read Ordering -- Defined in ‘GHC.Read’
        instance Read Integer -- Defined in ‘GHC.Read’
        instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’
        ...plus 22 others
        ...plus 7 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the first argument of ‘show’, namely ‘(read "5")’
      In the expression: show (read "5")

Prelude
<λ> :set -XExtendedDefaultRules

Prelude
<λ> :t show (read "5")
show (read "5") :: String

Prelude
<λ> show (read "5")
"*** Exception: Prelude.read: no parse

Prelude
<λ> show (read "()")
"()"

@Wizek
Copy link
Author

Wizek commented Nov 10, 2017

Thanks @mrkgnao for your comment, and sorry for my belated reply. I unfortunately don't yet see how your example is applicable in my case above.

For one, for instance, my type error mentions the mysterious is as general as its infrared signature bit, which I don't see in your example. Can you make your example include that in its error? Or tell me what that means in my code?

For second, as you show, you can actually attempt to execute the ill-typed deferred-errorful code by:

<λ> show (read "5")
"*** Exception: Prelude.read: no parse

And I believe my example produces the type error in a dead-code section: meaning it could actually never be reached. I suspect the compiler is preventing a valid program from compiling. If you suspect the opposite, could you perhaps show how this deferred type error could be reached in my example?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment