Cat Plus Plus has already explained why
getNextState :: GameState b => GameStateMonad a (Maybe b)getNextState = return (Nothing :: Maybe Blank)
doesn't work, so I can be short on that. The type signature promises that getNextState
can deliver a value of type Maybe b
for whatever type b
the caller demands. It's the caller of the function who decides what type it shall return if a function has polymorphic return type. So the signature promises "whatever you want, as long as it's a GameState
instance", but the implementation says "No, I don't care what you ordered, I return a Blank
".
Ambiguous type variable `b0' in the constraint: (GameState b0) arising from a use of `getNextState'Probable fix: add a type signature that fixes these type variable(s)
from typing
runStateT getNextState Blank
at the ghci prompt. If you ask ghci for the type of that, it will tell you
runStateT getNextState Blank :: GameState b => IO (Maybe b)
(no guarantees about the choice of type variable). But there is no context, so ghci doesn't know which type to instantiate b
with. And so it doesn't know which implementation of getNextState
it should call [or, which dictionary it should pass, if we look at GHC's implementation of type classes]. It has no way of resolving that ambiguity, so it tells you about it and suggests how you could resolve it.
test :: Num a => Maybe atest = Nothing
Yes, that's in principle the same problem, when you type test
at the ghci prompt. But there are special rules for resolving ambiguous type variables when there is one numeric class involved (where ambiguities are most frequent, literals are already ambiguous), and all involved constraints are simple and concern classes of the Prelude or the standard libraries. In that case, ambiguous type variables are instantiated by defaulting, so ghci would choose to instantiate a
with Integer
and print the Nothing
of the type Maybe Integer
.
Your GameState
class is not defaultable, that's the difference between these examples.
Why does this error occur without me calling a function on the returned polymorphic data, as explained here?
Because you're not calling any function that would determine the type. If you had a function of type
foo :: Blank -> Int
and typed
runStateT getNextState Blank >>= print . maybe 0 foo
the use of foo
would determine b
and all would be swell.
The problem would however not be solved but exacerbated, if you called a polymorphic function (whose argument type cannot be inferred from its result type), as in the linked example. Then the ambiguous type is no longer reachable from the outside, and it can never be resolved. Then the only way is to provide a type signature that resolves the ambiguity.
Doesn't this mean that functions that return a polymorphic result are essentially useless?
Oh no, they are immensely useful. Look at read
, or fromInteger
, realToFrac
, ...
The point is, that the type at which they shall be used must somehow be determined where they are used. Most of the time that is done by the calling context, but sometimes an explicit type signature is necessary.