Description
Hi there,
I was implementing a trie/prefix tree data structure (here) and, while considering how to implement fold on the data structure, I think I noticed a conceptually simple defunctionalisation technique that doesn't require runtime dispatching on a datatype.
The kind of transformation I have in mind would turn the higher-order function Vector.foldl
(for example) from:
Vector.foldl (fn (elt, acc) => elt + acc)
to:
signature VECTOR_FOLDL =
sig
type vectorElementType
type environment
type accumulator
val folder : vectorElementType * environment * accumulator -> accumulator
end
functor MakeVectorFoldl (Fn : VECTOR_FOLDL) =
struct
fun helpFoldl (pos, env, acc, vec) =
if pos = Vector.length vec
then acc
else
let
val elt = Vector.sub (vec, pos)
val acc = Fn.folder (elt, env, acc)
in
helpFoldl (pos + 1, env, acc, vec)
end
fun foldl (env, initial, vec) = helpFoldl (0, env, initial, vec)
end
structure PlusFolder =
struct
type vectorElementType = int
type environment= unit
type accumulator = int
fun folder (elt, _, acc) = elt + acc
end
structure PlusVectorFoldl = MakeVectorFoldl (PlusFolder)
(* higher order function which I think will not have a runtime dispatch cost *)
val foldl = PlusVector.foldl
so that MLton's defunctoriser in the elaborate pass doesn't need to construct a sun type over every higher order function and dispatch to the appropriate function at runtime.
I would like to have a go at implementing this kind of optimisation in MLton myself, but I might give up considering my total inexperience with coding compilers 😅 so I thought it wouldn't be bad to post it here in the event I get frustrated and fail, and there is interest from others in implementing the idea.
The transformation looks general to me (can mutate values in the environment record if user wants) and doesn't seem to require anything fundamentally novel to implement, but I would be interested in hearing criticism if it's not possible too.