[comp.lang.functional] Type Inference in ML

mmcg@bruce.cs.monash.OZ.AU (Mike McGaughey) (01/17/91)

There *is* an anomaly in the LML type inference algorithm - one which
has recently prevented my use of the language for some very elegant
continuation based solutions to problems.  The simplest form of the
problem is that the expression

	let x = hd o hd in x

will typecheck properly - to (List (List *a))->*a) - whereas

	(\x.x o x) hd

will not.  The problem with the second expression seems to be that the
inference algorithm erroneously expects both of the 'x' values in the
x o x to have the same type signature, when in fact it is only the _form_
of the signature that should be shared (perhaps a new set of type
variables should be created for each occurrence of x, and then unified
in the normal way).  Thus the type of x in the expression is given as
(*a->*a).

I have three questions: can anyone point me to an algorithm or paper
describing a polymorphic type system which does not share this
anomaly?  Are there any readily available lazy functional languages
which use this system?  Has anyone modified the LML compiler to do
this? (looks around hopefully... :-)

Cheers,

    Mike.
-- 
Mike McGaughey			ACSNET:	mmcg@bruce.cs.monash.oz

"His state is kingly; thousands at his bidding speed,
 And post o'er land and ocean without rest."		- Milton.

brian@comp.vuw.ac.nz (Brian Boutel) (01/17/91)

In article <3603@bruce.cs.monash.OZ.AU>, mmcg@bruce.cs.monash.OZ.AU
(Mike McGaughey) writes:
|> There *is* an anomaly in the LML type inference algorithm - one
|> which
|> has recently prevented my use of the language for some very elegant
|> continuation based solutions to problems.  The simplest form of the
|> problem is that the expression
|> 
|> 	let x = hd o hd in x
|> 
|> will typecheck properly - to (List (List *a))->*a) - whereas
|> 
|> 	(\x.x o x) hd
|> 
|> will not.  The problem with the second expression seems to be that
|> the
|> inference algorithm erroneously expects both of the 'x' values in
|> the
|> x o x to have the same type signature, when in fact it is only the
|> _form_
|> of the signature that should be shared (perhaps a new set of type
|> variables should be created for each occurrence of x, and then
|> unified
|> in the normal way).  Thus the type of x in the expression is given
|> as
|> (*a->*a).
|>

This is NOT erroneous. It is quite normal Hindley/Milner typing, and
other functional languages behave the same way. It is usually expressed
by saying that the type variables associated with lambda-bound variables
are non-generic.

Indeed it is the reason why functional languages need the let (or where)
syntax. In all respects other than type inference, let a = b in c is
equivalent to (\a.c)b. If you want the type behaviour of the let form,
use it.

The type given to \x.x o x  is Forall t. (t -> t) -> (t -> t), but it
would appear that a more general type is  Forall T,t. (T t -> t) -> (T(T
t) -> t), where T is a type constructor variable, e.g T could
instantiate to List or Tree. etc. Unfortunately, quantification over
type constructors is second order, and undecidable.

--brian
-- 
Internet: brian@comp.vuw.ac.nz
Postal: Brian Boutel, Computer Science Dept, Victoria University of Wellington,
        PO Box 600, Wellington, New Zealand
Phone: +64 4 721000

farrell@batserver.cs.uq.oz.au (Friendless) (01/17/91)

In <3603@bruce.cs.monash.OZ.AU> mmcg@bruce.cs.monash.OZ.AU (Mike McGaughey) writes:
>	let x = hd o hd in x
>will typecheck properly - to (List (List *a))->*a) - whereas
>	(\x.x o x) hd
>will not.  The problem with the second expression seems to be that the
>inference algorithm erroneously expects both of the 'x' values in the
>x o x to have the same type signature, 

  This is the correct behaviour of Milner typing - parameters only have one
type. I was surprised too when I found it out, but I think it complicates the
algorithm a lot to fix it.


Friendless

kh@cs.glasgow.ac.uk (Kevin Hammond) (01/17/91)

In article <3603@bruce.cs.monash.OZ.AU> mmcg@bruce.cs.monash.OZ.AU (Mike McGaughey) writes:
>There *is* an anomaly in the LML type inference algorithm - one which
>has recently prevented my use of the language for some very elegant
>continuation based solutions to problems.  The simplest form of the
>problem is that the expression
>
>	let x = hd o hd in x
>
>will typecheck properly - to (List (List *a))->*a) - whereas
>
>	(\x.x o x) hd
>
>will not.  

NO, LML is doing the "right thing".  This is not a problem with LML,
it's a problem with Hindley/Milner polymorphic type inference.
let-bound definitions are polymorphic (ignore letrec, which is a
special case), whereas lambda-bound definitions are monomorphic.

The people at Nijmegen (Eric Nocker/Sjaak Smetsers if my memory's
correct) have a more permissive type system implemented for the Clean
GRS (based on "intersection types"), but it has a few vices of its
own...  [try rinus@cs.kun.nl -- Rinus Plasmeijer -- I don't have
Eric's address handy].

Maybe you should be using an untyped language (LML with typechecking
off?).

Kevin
-- 
This Signature Intentionally Left Blank.	E-mail: kh@cs.glasgow.ac.uk

kfr@dip.eecs.umich.edu (K. Fritz Ruehr) (01/19/91)

  mmcg@bruce.cs.monash.OZ.AU (Mike McGaughey) writes:

>	I have three questions: can anyone point me to an algorithm or paper
>	describing a polymorphic type system which does not share this
>	anomaly?  Are there any readily available lazy functional languages

  I don't know if he has implemented the type system for a programming
language, but a system of conjuctive types devised by R. Ghosh-Roy at Essex
attempts to resolve just the problem you describe.  The system is written up
in:

  "Conjunction-Type Standard ML Polymorphism", R. Ghosh-Roy.
  LISP and Symbolic Computation: An International Journal, 3, 381-409, 1990.
  Kluwer Academic Publishers.

His address is given at the front of the paper as:

  R. Ghosh-Roy  (ghosr@essex.ac.uk)
  Room Number 4B.526, Department of Computer Science
  University of Essex, Wivenhoe Park
  Colchester, CO4 3SQ, Essex, England, UK

He claims the following results for his system: decidability (of well-typing),
semantic soundness for all types, semantic soundness and completeness for 
basic types.

  --  Fritz Ruehr
      kfr@dip.eecs.umich.edu

doug@nixtdc.uucp (Doug Moen) (01/19/91)

>In article <3603@bruce.cs.monash.OZ.AU>, mmcg@bruce.cs.monash.OZ.AU
>(Mike McGaughey) writes:
>|> There *is* an anomaly in the LML type inference algorithm - one which
>|> has recently prevented my use of the language for some very elegant
>|> continuation based solutions to problems.  The simplest form of the
>|> problem is that the expression
>|> 
>|> 	let x = hd o hd in x
>|> 
>|> will typecheck properly - to (List (List *a))->*a) - whereas
>|> 
>|> 	(\x.x o x) hd
>|> 
>|> will not.  The problem with the second expression seems to be that the
>|> inference algorithm erroneously expects both of the 'x' values in the
>|> x o x to have the same type signature, when in fact it is only the _form_
>|> of the signature that should be shared (perhaps a new set of type
>|> variables should be created for each occurrence of x, and then unified
>|> in the normal way).  Thus the type of x in the expression is given as
>|> (*a->*a).

In article <1991Jan16.225903.2540@comp.vuw.ac.nz>, brian@comp.vuw.ac.nz
(Brian Boutel) writes:
>This ... is quite normal Hindley/Milner typing, and other functional
>languages behave the same way. It is usually expressed by saying that
>the type variables associated with lambda-bound variables are non-generic.
>
>Indeed it is the reason why functional languages need the let (or where)
>syntax. In all respects other than type inference, let a = b in c is
>equivalent to (\a.c)b. ...
>
>The type given to \x.x o x  is Forall t. (t -> t) -> (t -> t), but it
>would appear that a more general type is  Forall T,t. (T t -> t) -> (T(T
>t) -> t), where T is a type constructor variable, e.g T could
>instantiate to List or Tree. etc. Unfortunately, quantification over
>type constructors is second order, and undecidable.

In the latest issue of Lisp and Symbolic Computation (3, 1990),
R. Ghosh-Roy presents a new type-inference system based on conjunctive
types.  His system finds more general types, and can typecheck
a larger set of programs, than the Hindley/Milner system.  His system
is decidable and semantically sound for all types.  As far as I can tell,
'let a = b in c' is completely equivalent to '(\a.c)b' in his system.

The GR system gives '\x.x o x' the type '((*a->*b) + (*b->*c)) -> (*a->*c)'.
The + means type conjunction.  This type can be read as meaning:
  the first occurrence of 'x' in the function has type (*a->*b)
  the second occurrence of 'x' in the function has type (*b->*c)

Under GR, the expression (\x.x o x)(hd) is well typed.  Given:
  (\x.x o x) : ((*a->*b) + (*b->*c)) -> (*a->*c)
  hd : (List *d)->*d
the unification algorithm is able to find the type assignments
  *a = List(List *e)
  *b = List(*e)
  *c = *e
which results in (\x.x o x)(hd) being assigned the type List(List *e)->*e.

So ...
Is conjunctive type inference as good as it seems?
Can we expect a new family of functional languages with conjunctive type
systems to appear?  Can SML (with its module system) and Haskell (with its
type classes) be cleanly extended to use conjunctive types?
-- 
Doug Moen
{mnetor,alias,geac,torsqnt,lsuc}!nixtdc!doug
77 Carlton #1504, Toronto, Ontario, Canada M5B 2J7

ramaer@cs.utwente.nl (Mark Ramaer) (01/21/91)

In article <3603@bruce.cs.monash.OZ.AU>, mmcg@bruce.cs.monash.OZ.AU
(Mike McGaughey) writes:
|> There *is* an anomaly in the LML type inference algorithm - one
which
|> has recently prevented my use of the language for some very elegant
|> continuation based solutions to problems.  The simplest form of the
|> problem is that the expression
|> 
|> 	let x = hd o hd in x
|> 
|> will typecheck properly - to (List (List *a))->*a) - whereas
|> 
|> 	(\x.x o x) hd
|> 
|> will not.  The problem with the second expression seems to be that
the
|> inference algorithm erroneously expects both of the 'x' values in
the
|> x o x to have the same type signature, when in fact it is only the
_form_
|> of the signature that should be shared (perhaps a new set of type
|> variables should be created for each occurrence of x, and then
unified
|> in the normal way).  Thus the type of x in the expression is given
as
|> (*a->*a).
|> 
|> I have three questions: can anyone point me to an algorithm or paper
|> describing a polymorphic type system which does not share this
|> anomaly?  Are there any readily available lazy functional languages
|> which use this system?  Has anyone modified the LML compiler to do
|> this? (looks around hopefully... :-)
|> 
|> Cheers,
|> 
|>     Mike.

Read the PhD thesis "M. Gerritsen, Type Assignment Functions" 1988.
University of Twente

She built a type inference algorithm where the occurrences of variables
not necessarily have the same type.
Instead she uses `conjuction types': *a & *b .
(\x.x o x) has the type ((*b->*c)&(*a->*b))->*a->*c which sais that x
must be
instantiatable as *a->*b and as *b->*c .
Application of (\x.x o x) to hd yields the equations:
	*a->*b = List *d->*d  (and so: *a=List *d,   *b=*d)
	*b->*c = List *e->*e  (and so: *b=List *e,   *c=*e)
The two occurrences of x have types List *d->*d and List *e->*e
respectively.
Solving the equations yields:
	*a=List (List *e)
	*b=List *e
	*c=*e
	*d=List *e
and so
	(\x.x o x) hd::*a->*c = List (List *e)->*e

In this type inference system there is no more need for an explicit
let-construct.
All Milner-typable expressions are also typable in this scheme, but
some
non Milner-typable expressions too.
Try `twice twice k' where
	twice f x = f (f x)
	k x y = x

Mark Ramaer

joosten@cs.utwente.nl (Stef Joosten) (01/23/91)

  mmcg@bruce.cs.monash.OZ.AU (Mike McGaughey) writes:

>	can anyone point me to an algorithm or paper
>	describing a polymorphic type system which does not share this
>	anomaly?

A very nice solution is offered in the thesis of Mirjam Gerritsen:

   M. Gerritsen
   Type Assignment Functions
   PhD thesis, University of Twente, Comp. Sc., the Netherlands, 1988.

postal address:
   Functional Programming Group, Comp. Sc.
   University of Twente,
   postbus 217
   7500 AE  ENSCHEDE
   the Netherlands

In this thesis Mirjam Gerritsen gives a typing algorithm (proof included),
and shows that Mike's problem is covered, and also proves that
Milner/Hindley typing is still completely covered.
Furthermore, she shows how to build an efficient implementation.

Good luck to all of you.
Stef Joosten