Custom operators in the revised syntax
[
Home
]
[ Index:
by date

by threads
]
[ Message by date: previous  next ] [ Message in thread: previous  next ] [ Thread: previous  next ]
[ Message by date: previous  next ] [ Message in thread: previous  next ] [ Thread: previous  next ]
Date:  20070512 (05:45) 
From:  skaller <skaller@u...> 
Subject:  Re: [Camllist] Custom operators in the revised syntax 
On Sat, 20070512 at 05:47 +0100, Jon Harrop wrote: > > I have only seen one algorithm for this an it was > > extremely complicated. If you know a better way I'd > > sure like to know what it is. > > AFAIK, you just change the unify to intersect sets of types. I have no idea how to unify in the presence of (undiscriminated) setwise union of polymorphic types. It seems a little easier if the types are restricted to a finite set of nonpolymorphic types. > I assume the > implementation of polymorphic variants already does this, so take that and > make it enforce a single type contructor at function boundaries (maybe > defaulting in some cases). If you "polymorphic variant" has more than one > constructor then there is an ambiguity and you flag an error asking for a > type annotation. The difference is that polymorphic variants HAVE constructors. But we're talking about types *without* constructors. In Felix, the (Ocaml) term combinators actually support typesets. But the unification engine just throws up when it sees them :) During overload resolution, you CAN use typesets as constraints. For example: typedef ints = typesetof(int, long); fun f[t in ints]: t * t > t = "$1+$2"; print$ f 1; // OK! print$ f 1L; // OK! print$ f 1.0; // ERROR fun g[t in ints](x:t)=> f x; // ERORR The last line illustrates the lack of propagation: the type of x is actually just 't' at the point f is applied, and t isn't in the set ints. The way it works is the argument is first matched against the unconstrained type (by unification), and then the constraint such as: int in typeset(int,long) is checked: if it fails that overload candidate is rejected (but another may still match). In principle the correct 'subtyping' rule is to use a subset (or superset depending on +/ position I guess) so that it should be possible in this case to propagate and make the function g above work. The problem is that once you introduce polymorphism, the type of one function can depend on the type of another, but that type isn't known until that other is actually instantiated. This can not be handled 'properly' by just adding typesets to the type system because that would be too general: it fails to record the dependencies. For example in the f/g case above, the type of g is NOT g: ints > ints but rather g: { int > int; long > long } and now consider a harder case like: t in ints. t * t = { int * int; long * long } which is quite different to ints * ints = { int * int; int * long; long * int; long * long } So roughly .. we're talking about a unification engine that can propagate constraints, and a type system which would seem to at least need to be second order. As I said I think this is possible for finite ground types, but in the presence of polymorphism unification would not be able to reduce many terms. In turn, this would make overloading such cases impossible. In Felix, overloading is done polymorphically, and can't vary depending on the instantiation of type variables (you can do that with typeclasses though). But unless you instantiate the type variables, you can't reduce the terms. It isn't clear it is even possible to normalise them for equality check, let alone tell if one is a specialisation of another. The result, in Felix, is that overloading would have to return a SET of functions instead of a single one, since several functions 'might' match the argument type. And the set could be large  it seems exponential in the length of a call chain. Ouch. If ANYONE knows an algorithm that can do unification with sets of types, that is, with a union type, I'd sure like to know it! > .NET provides runtime type information so I assume the compiler specializes > when types are static and resorts to runtime dispatch otherwise. That seems right. I think G'Caml tried to do that was well. > > and this kind of constraint, unfortunately, doesn't propagate > > (i.e. the above isn't really a type). Propagating ground > > type sets is actually easy, but once you have higher orders > > it is probably undecidable. > > I hadn't thought of that. I just discovered that you cannot add vectors of > vectors in F#, so it is probably 1st order only. Hmm, that's a bit surprising if your explanation above is correct, that is, if it resorts to dynamic typing if it can't resolve statically.  John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net