Re: Writing a counter function

Mark Carroll wrote:
On Sat, 29 Jun 2002, Samuel E. Moelius III wrote: (snip)
Here's another not-exactly-what-you-wanted solution. :) (snip)
Do any of the experimental extensions to Haskell allow a what-he-wanted solution? I couldn't arrange one in H98 without something having an infinitely-recursive type signature.
That's because the problem requires an infinitely-recursive type.
I'm sure it would have been easy in Lisp, and he already gave a Perl equivalent,
That's because both Lisp and Perl are weakly typed. Haskell is strongly typed. Consider Lisp: (defun self-apply (f) "Apply F to itself." (f f)) No problem (and I'm sure you can pull the same trick off in Perl). Consider Haskell: self-apply f = f f The *typing algorithm* (the thing that didn't complain in Lisp) proceeds roughly as follows: f is applied to at least one argument, so f must have type a -> b. Therefore, f's argument (f) must have type a. So, we conclude: f :: a -> b f :: a But f can only have one type, so we set its types equal: a = a -> b This is clearly recursive, right?
so I'm wondering if it could be at all sane for Haskell to allow such stuff
Sure. All it has to do is: 1. Create its own newtype in response to such things as `self-apply' above. 2. Ensure that self-apply f = f f and self-apply' g = g g have the same type. I would *love* to hear ideas on how to do that, but it's difficult.
and if Haskell is somehow keeping us on the straight and narrow by disallowing the exact counter that was originally requested.
Well, it's a more general problem than the `exact counter'. And, yes, Haskell is erring on the side of safety here; there's a reason it's called a ``statically typed language''.
The beauty of his request was that it was so simple and seemed to make sense; I went ahead and tried to fulfill it before realising I couldn't do it either.
Well, it makes sense, yes. But I think learning to manually `unify' these things (add a newtype) is not hard, and probably useful --- Haskell is not the only statically-typed language out there. Learn how to solve this problem, and you understand the problem, the solution, typing, and life in general :)
-- Mark
Jon Cast

On Sun, 30 Jun 2002, Jon Cast wrote:
Mark Carroll wrote:
On Sat, 29 Jun 2002, Samuel E. Moelius III wrote: (snip)
Here's another not-exactly-what-you-wanted solution. :) (snip)
Do any of the experimental extensions to Haskell allow a what-he-wanted solution? I couldn't arrange one in H98 without something having an infinitely-recursive type signature.
That's because the problem requires an infinitely-recursive type.
Consider Haskell:
self-apply f = f f
The *typing algorithm* (the thing that didn't complain in Lisp) proceeds roughly as follows:
f is applied to at least one argument, so f must have type a -> b. Therefore, f's argument (f) must have type a. So, we conclude:
f :: a -> b f :: a
But f can only have one type, so we set its types equal:
a = a -> b
This is clearly recursive, right?
so I'm wondering if it could be at all sane for Haskell to allow such stuff
Sure. All it has to do is:
1. Create its own newtype in response to such things as `self-apply' above.
2. Ensure that
self-apply f = f f
and
self-apply' g = g g
have the same type. I would *love* to hear ideas on how to do that, but it's difficult.
It isn't particularly hard to implement this. Haskell typecheckers use unification to match types up; the only difference is that a graph unification algorithm would be needed instead. Such algorithms exist --- the only real difference is you have to remember what you're unifying to avoid falling into a loop when unifying graphs with cycles. The idea of adding this to Haskell has been proposed several times, but never implemented. And the reason for THAT is that it would make type errors significantly harder to understand. Consider f x y = if x==0 then y else f (x-1) (where I "forgot" the second parameter in the recursive call). We expect this to be a type error, and indeed it is --- BECAUSE recursive types are not allowed. If they were, this definition would be well-typed, with the type f :: Num a => a -> b where b = b -> b You wouldn't get an error message unless you tried to CALL f, and perhaps not even then. For example, f 1 2 :: Num b => b where b = b -> b is well-typed! So you would probably eventually, somewhere else in the program, see an error message of the form ERROR - Illegal Haskell 98 class constraint in inferred type *** Type : Num b => b where b = b -> b This is enough to scare most people off the idea. Think of all the times you've seen the error message unification would give infinite type It's not the most common kind of type error, but it does happen regularly, at least to me. In every such case, the type error would be deferred in the way I describe. If I remember rightly, OCaml allows type recursion of this sort, but restricts it to object types precisely to avoid these problems. John Hughes

John Hughes wrote:
On Sun, 30 Jun 2002, Jon Cast wrote:
Mark Carroll wrote:
On Sat, 29 Jun 2002, Samuel E. Moelius III wrote: (snip)
Here's another not-exactly-what-you-wanted solution. :)
(snip)
Do any of the experimental extensions to Haskell allow a what-he-wanted solution? I couldn't arrange one in H98 without something having an infinitely-recursive type signature.
That's because the problem requires an infinitely-recursive type.
(snip)
It isn't particularly hard to implement this. Haskell typecheckers use unification to match types up; the only difference is that a graph unification algorithm would be needed instead. Such algorithms exist --- the only real difference is you have to remember what you're unifying to avoid falling into a loop when unifying graphs with cycles.
The idea of adding this to Haskell has been proposed several times, but never implemented. And the reason for THAT is that it would make type errors significantly harder to understand.
(snip)
Yes, but the dreaded Monomorphism Restriction was added for the same reason, wasn't it? Haskell allows us to get around the M.R. by using explicit type signatures where required. It seems to me that we could allow recursive types in the same way -- simply require a type signature for toplevel objects with recursive types. Is there a reason why this wouldn't work? Regards, Matt Harden
participants (3)
-
John Hughes
-
Jon Cast
-
Matt Harden