Newsgroups: comp.lang.misc Path: utzoo!utgpu!watserv1!watmath!watmsg.waterloo.edu!jlophel From: jlophel@watmsg.waterloo.edu (John Ophel) Subject: Re: A Hard Problem for Static Type Systems Message-ID: <1991Apr22.151149.9661@watmath.waterloo.edu> Sender: news@watmath.waterloo.edu (News Owner) Organization: University of Waterloo References: <1991Apr20.010347.28984@leland.Stanford.EDU> Date: Mon, 22 Apr 1991 15:11:49 GMT Lines: 133 > Article: 7556 of comp.lang.misc > From: craig@leland.Stanford.EDU (Craig Chambers) > Here's a simple example that cannot be described by a static type > system in most statically-typed object-oriented languages. > Consider the general min function (written in a dynamically-typed > C-like language): > min(x, y) { > if (x < y) { > return x; > } else { > return y; > } > } > So here are some examples that should type-check: > min(3, 4) > min(3, 4.5) > min({3,4}, {4,5,6}) > min({{3,4.5},{5},{6,8.9}}, {{1.2,4},{2}}) > And here are some that shouldn't: > min(3, {4}) > min({3,4}, {{4,6},{2,3.4,6}}) > -- Craig Chambers Here is a solution in watML, an experimental dialect of ML developed here at the University of Waterloo. watML basically adds ideas presented in the paper by Cormack and Wright "Type-dependent Parameter Inference" SIGPLAN'90 Conference to ML. The work on watML is being done by Gord Cormack and me. The first problem is that if min (3,4.5) is permitted, then what is the result type of min (integer or real)? The solution in ML is to introduce a discriminated (tagged) union - datatype number = Int of int | Real of real; Now min (Int 3, Real 4.5) will return a number object. watML adds overloading to ML. - fun min (x,y) = if x < y then x else y; > val min = [?<: ('a * 'a) -> bool]('a * 'a) -> 'a The reading of the type of min is, if an appropriate instance of < can be found by searching the environment, then min is a function that takes two arguments of the same type and returns the lesser (as defined by the < function that was chosen). An appropriate instance of < is selected based on having a matching type. [As an aside, min is implemented by the compiler as a function that takes an extra parameter (<), and for any use of min, the compiler automatically supplies the appropriate < function to use.] We now define an instance working over numbers (the overload construct is not in ML) - overload < = fn (Int i, Int j) => i < j | (Int i, Real r) => (real i) < r | (Real r, Int i) => r < (real i) | (Real r, Real s) => r < s; > val < = fn : (number * number) -> bool In ML syntax "fn" is "lambda", so if < gets two nums, it pattern-matches on their constructors and selects the appropriate instance of < to use. For example, the first case will select integer < as i and j are both integers. The next three cases will select real <. We can now define an instance working over lists - overload < = fn ([],[]) => false | ([],_) => true | (_,[]) => false | ((a::b),(c::d)) => (a val < = [?<: ('a * 'a) -> bool](('a list) * ('a list)) -> bool Notice that in the last case, there are two uses of <, the first cannot be resolved (as the list arguments are polymorphic), the second is the instance we are defining (< over lists). The unresolved instance of < is reflected in the type of this instance that requires an instance of <. - min (Int 3, Int 4); Int 3 : number - min (Int 3, Real 4.5); Int 3 : number - min ([Int 3, Int 4],[Int 4, Int 5, Int 6]); [Int 3, Int 4] : number list - min ([[Int 3, Int 4, Int 5],[Int 5], [Int 6, Real 8.9]], [[Real 1.2,Int 4],[Int 2]]); [[Real 1.2,Int 4],[Int 2]] : (number list) list but - min (Int 3, [Int 4]); - min ([Int 3, Int 4], [[Int 4, Int 6], [Int 2, Real 3.4, Int 6]]); both give type errors. Comments ======== - I have not attempted to explain how overloaded instances are resolved. We are currently working on a paper that describes the system. - There are problems with overloading resolution, the selection process can get into an infinite loop (in obscure cases). This needs to be investigated. - Static typing solutions are occassionally more involved than dynamic typing solutions. As more work is done with static systems, however, more and more problems that were previously clumsy or difficult with static typing can be naturally expressed with static typing; for example, ML's parameteric polymorphism permits polymorphic functions to be written with the security of static typing; ML's type inference removes the need for redundant type declarations; watML adds overloading to a static type system. Experience with dynamic type systems has suggested valuable concepts that languages should be able to support; modern static type systems are attempting to provide these concepts within secure, efficient systems. - Overloading with type classes is part of the Haskell language definition (see the Haskell report or Blott & Wadler's very readable POPL'89 paper). John Ophel