Seemingly impossible functional programs (2007)
math.andrej.com
math.andrej.com
As a result, laziness reveals information about a function's implementation in a way that strict evaluation does not. It matters which parts of the input a function actually reads.
Similarly, in non-lazy languages, functions that take callbacks reveal information about themselves by calling the callback (or not). We can write tests demonstrating the order in which a function calls its callback.
I wrote up a Python gist (https://gist.github.com/evanpw/d69f1ccb5edd0b672e9e) which performs this same trick by explicitly snooping on the callbacks. It seems a lot less magical than the Haskell version.
EDIT: The main "cheat" seems to be the ability to identify the predicate which returns false on every input (e.g., by seeing if returns false without evaluating its argument). The continuity argument described in the article basically says that if p is not always false, then there's some input a with a(n) = 0 for all sufficiently large n that causes p to return true, so you could just enumerate all such inputs until one returns true.
Perhaps this relies subtly on Haskell's laziness? If so, I don't think there is a "quick translation" to ML or OCaml.
EDIT: I think I see the light. The result of find_i(...) is not what's sent to p. Instead, Haskell passes a thunk that might never need to be evaluated.
let f x = 1
print (f (g somethingelse))
then the call to g is never evaluated. f returns 1 immediately.> Common wisdom tells us that function types don’t have decidable equality. In fact, e.g. the function type Integer -> Integer doesn’t have decidable equality because of the Halting Problem, as is well known. However, common wisdom is not always correct, and, in fact, some other function types do have decidable equality, for example the type Cantor -> y for any type y with decidable equality, without contradicting Turing [...] This seems strange, even fishy, because the Cantor space is in some sense bigger than the integers. In a follow-up post, I’ll explain that this has to do with the fact that the Cantor space is topologically compact, but the integers are not.
I wonder if they got any interesting results from this investigation. There are some practical applications which works on infinite data (such as streams), but I don’t see immediately how this applies there.
Did this ever happen? I haven't seen it.
The article predates C++11 by many years, and with functions-as-values and lambdas now in the language, would be interesting to see if it could also be translated to C++.
It's important to remember local objects with destructors will make this impossible, since destructors are run at the end of the function scope and thus after what's seemingly a perfect tail call.
As for Maybe, if it is basically an "optional" as in other languages, then the newer c++17 has <optional>, if I'm not mistaken.