[MLton] funny error message

Stephen Weeks MLton@mlton.org
Sat, 24 Sep 2005 22:05:08 -0700


> Ok, here is a pretty minimal example that produces the bad error
> message.

Now all is clear.  Here are the type schemes for flatmap and flatten
as determined by type inference on the structure.

  flatmap: forall 'a. ('a -> X stream) -> ('a stream -> X stream)
  flatten: X stream stream -> X stream

Here, X indicates a single unspecified type (not a generalizable type
variable).

The signature requires the type schemes for flatmap and flatten to be
the following.

  flatmap: forall 'a, 'b. ('a -> 'b stream) -> ('a stream -> 'b stream)
  flatten: forall 'a. 'a stream stream -> 'a stream

When matching, MLton first tries to match flatmap in the structure
against the signature.  This is done by instantiating flatmap in the
structure with a fresh unknown type, Y, and unifying the resulting
type

  (Y -> X stream) -> (Y stream -> X stream)

with the type of flatmap needed for the signature

  ('a -> 'b stream) -> ('a stream -> 'b stream)

The unification causes X = 'b and Y = 'a.  At this point the type of
flatmap becomes

  flatten: forall 'a. ('a -> 'b stream) -> ('a stream -> 'b stream)

Because 'b is not in the type scheme, MLton reports the following
error.

  Variable type in structure disagrees with signature.
    variable: flatmap
    unable to generalize: 'b
    signature: ('a -> 'b stream) -> ('a stream -> 'b stream)

If we stopped here, all would be well.  But, since MLton likes to
report as many errors as possible, it continues by comparing the type
of flatten in the structure

  flatten: 'b stream stream -> 'b stream

with the type scheme in the signature

  flatten: forall 'a. 'a stream stream -> 'a stream

In this case, there is nothing to instantiate, since the type in the
structure is a monotype, so MLton simply unifies

  'b stream stream -> 'b stream

with
  
  'a stream stream -> 'a stream

This gives the error that you saw

  Variable type in structure disagrees with signature.
    variable: flatten
    structure: ['a] stream stream -> ['a] stream
    signature: ['b] stream stream -> ['b] stream

The names for the tyvars appear switched because MLton chooses those
names at the last possible minute and only for printing an error
message.  The tyvars are really just distinct objects inside.

In short, the problem is a spurious error due to the prior error.  As
a reminder, it is always best to read errors from first to last, since
later errors may be spurious.

There are two ways I could see fixing this bug.

1. During the matching of flatmap, we could avoid unifying X because
   we know that it is not generalizable due to its occurrence in
   flatten.  We would still report the "unable to generalize" error on
   flatmap.  The matching of flatten would automatically do the right
   thing. 

2. We could allow the matching of flatmap to proceed, but notice
   during the matching of flatten that there are ungeneralized type
   variables.

(1) seems better because the type error for both flatten and flatmap
will be the same, while (2) will cause there to be a different error
for flatten.

I'm not sure how viable either of these really is, but I'll think
about it.  I'd like to hear other ideas too.