Hard-to-read/misleading errors for types that are missing an import and interpreted as variables
This is something that has tripped me up a few times recently, so I thought it might be worth filing a report for it. Even though the frontend technically exhibits the correct behavior, the error messages are highly misleading (and for larger programs often hard-to-read too).
Consider the following program:
import Data.IORef (newIORef)
f :: Int -> IO (IORef Int)
f n = do
x <- newIORef 42
return x
The programmer forgot to import the IORef
type, otherwise the program should be correct. GHC outputs a helpful note here:
src/MissingImport.hs:3:17: error: [GHC-76037]
Not in scope: type constructor or class ‘IORef’
Suggested fix:
Perhaps you want to add ‘IORef’ to the import list in the import of
‘Data.IORef’ (src/MissingImport.hs:1:1-28).
|
3 | f :: Int -> IO (IORef Int)
| ^^^^^
Curry, on the other hand, emits this:
src/MissingImport.curry:3:6-3:26 Error:
Type signature too general
Function: f
Inferred type: Prelude.Int ->
Prelude.IO (Data.IORef.IORef Prelude.Int)
Type signature: Int -> IO (IORef Int)
|
3 | f :: Int -> IO (IORef Int)
| ^^^^^^^^^^^^^^^^^^^^^
ERROR occurred during parsing!
To the untrained eye, this error message is highly confusing, since it looks like the inferred and expected type are the same. The problem here is the interaction with a legacy feature (that may or may not be considered deprecated) that Curry supports different case modes and, by default, interprets even capitalized type identifiers that aren't otherwise imported as type variables. Therefore the second/expected IORef
is interpreted as a type variable here, in other words, the Curry compiler reads the program as
f :: Int -> IO (a Int)
f n = do
x <- newIORef 42
return x
Now we do have a warning for this and if we update the function to
f :: Int -> IO (IORef Int)
f = failed
the warning is emitted correctly too (since it now typechecks as Int -> IO (a Int)
too):
src/MissingImport.curry:3:17-3:21 Warning:
Symbol `IORef' is a variable name, but the selected case mode is `curry`, try renaming to iORef instead
|
3 | f :: Int -> IO (IORef Int)
| ^^^^^
The immediate fix I see here is to therefore move the case mode check before the type checker (or even earlier) and to find some way to emit this warning even when errors are emitted. In the long term, we might consider improving the typechecker's error messages too (maybe clarify which types are interpreted as variables?).