I stumbled upon this fun post at
Stack Overflow. Let me paraphrase the goal:
write a function contraption
that, when given another
function (call this the operator
)
and an argument x, invokes operator(x)
, and returns a function
that, when given yet another argument y, invokes operator(y)
,
and returns a function that, when given yet another argument z, invokes
operator(z)
, and returns a function that ...
That's kind of a gross definition. What we're looking to do, in ocaml, is the following:
let printer = contraption (print_endline) printer "Hello" "World" "How" "Is" "It" "Going?"
Clearly this should print 6 lines, with the relevant text. Cool, right? You
(kind of) get a function that takes an arbitrary number of parameters of the same
type (sort of, because you have to do the same thing to each one). So, how do we
write contraption
? As follows:
let rec contraption op x = let _ = op x in fun y -> contraption op y
So, what type does contraption
have? Well, this is where things get
loose. Run this through ocaml
and you get:
# let rec contraption op x = let _ = op x in fun y -> contraption op y;; Characters 60-76: fun y -> contraption op y;; ^^^^^^^^^^^^^^^^ Error: This expression has type 'a -> 'b but is here used with type 'b
What? 'a -> 'b
isn't the same as 'b
? I guess not,
but why not let 'a -> 'b
equal 'b
? Sure,
it's an infinite type, but it seems like everything would work out.
Indeed, if we run ocaml -rectypes
instead we get the following type:
val contraption : ('a -> 'b) -> 'a -> ('a -> 'c as 'c)
What does this type mean? It says that, "if we give it a function from foo to bar, and a bar, then we get back something of an infinite type". And this infinite type is "if you give me a foo, I'll give you back an infinite type", indeed the same infinite type. And that's just what we want!
So, if everything works fine with -rectypes
, why isn't it always
turned on? Apparently leaving it on all of the time can get you even worse
error messages than ocaml already gives you. Hard to believe, I know, but
nevertheless there is indeed a deeper circle of Hell.
To bring it back to Mix, we might wonder if this craziness will work in Mix, and how it will look if it does. Here you go:
contraption(op, x) { op(x); return function(y) { return contraption(op, y); }; };
Now, how do we use it?
Int main(String args) { var printer = function(s){ return contraption(IO.print, s); }; printer("Hello")("World")("This")("Is")("Bizarre"); return 0; };
Note that since we don't have currying in Mix we were obligated to
build an "extra" anonymous function when creating printer
;
if we had the placeholder syntax "_
" of
Scala
we could have written it as follows:
Int main(String args) { var printer = contraption(IO.print, _); printer("This")("is")("Cleaner"); return 0; };
Or, if you prefer we can tweak the definition of contraption slightly instead:
contraption(op) { return function(x) { op(x); return contraption(op); }; }; Int main(String args) { var printer = contraption(IO.print); printer("This")("Is")("Cleaner")("Still!"); return 0; };
And so Mix is victorious (or at least works, after a fashion) once again!
And incidentally, we see how nice currying can be.
PS - I definitely need to shorten the function
keyword; perhaps
just fn
?
No comments:
Post a Comment