(As always, this code is presented "as-is" and without warranty or implied fitness of any kind; use at your own risk.)
type X () =
let mutable a = [1;2;3;4;]
let mutable b = [];
let rec xfer1 () =
match a with
| [] -> ()
| h::t ->
xfer1 (a<-t;b<-h::b)
let rec xfer2 () () =
match a with
| [] -> ()
| h::t -> xfer2 (a<-t) (b<-h::b)
member this.Xfer1 () =
xfer1()
this
member this.Xfer2 () =
xfer2()()
this
let x1 = (X()).Xfer1()
let x2 = (X()).Xfer2()
printfn "Done."
However, I was concerned about what the generated code actually contained, so I decided to have a look at the IL. As it turns out, the two cases, xfer1 vs. xfer2, generate very different code. xfer1 results in a function of no arguments. xfer2, on the other hand, results in a function with two unit arguments.
// Signatures:
internal void xfer1()
internal void xfer2(Unit unitVar0, Unit unitVar1)
// As called:
this.xfer1();
this.xfer2(null, null);
xfer2, I’m sure, represents the canonical case and xfer1 the exception. This exception is very convenient, and it results in more efficient code. But I’m wondering why it wasn’t extended to include functions of any arity as long as all the arguments are unit? I’m sure this behavior is spelled out in the F# spec, and if I find the section, I’ll update this entry.
-Neil
No comments:
Post a Comment