mlton
mlton copied to clipboard
Consider Ref_alloc and Ref_uninit primitives
It would be possible to extend the ideas from #207 to references. For example, we could have:
structure Unsafe.Ref:
sig
val alloc: unit -> 'a ref
val uninit: 'a ref -> unit
structure Raw:
sig
type 'a rawref
val alloc: unit -> 'a rawref
val toRef: 'a rawref -> 'a ref
val uninit: 'a rawref -> unit
end
end
One potential motivation is to eliminate the (admittedly small) dispatch overhead in a ('a -> 'b) ref
that is only ever meant to hold one function. For example, recall the classic "back-patching" technique for recursion with first-class functions and references:
val fact_ref = ref (fn _ => raise Fail "fact")
val fact = fn n => if n <= 1 then 1 else n * (!fact_ref) (n - 1)
val () = fact_ref := fact
Control-flow analysis will determine that the contents of fact_ref
is either fn _ => raise Fail "fact"
or fn n => ...
, and the application (!fact_ref) (n - 1)
will have a case-analysis to select between these functions. [Note that the overhead is essentially the same, but more obvious to the programmer, when using an ('a -> 'b) option ref
.]
With a Ref_alloc
primitive, one could write:
val fact_ref = Unsafe.Ref.alloc ()
val fact = fn n => if n <= 1 then 1 else n * (!fact_ref) (n - 1)
val () = fact_ref := fact
Control-flow analysis will determine that the contents of fact_ref
is only fn n => ...
and the application will have a single (exhaustive) case-match. Subsequent ConstantPropagation and/or Useless optimizations would probably eliminate the reference entirely.
Another potential use of Ref_alloc
and Ref_uninit
would be to support Thread_copyCurrent
, which currently "returns" the copied thread via GC_getSavedThread
, but could be changed to take a thread ref
and write the copied thread there. Using Ref_alloc
to allocate the thread ref
would be convenient, because it is otherwise not easy to obtain a thread to initialize the ref (only to be overwritten).