Paul Graham once published an article about what he called "accumulator generators". This problem requires the existence of an unspecified numeric tower. Lisp happens to have one and it happens to be adequate for Paul Graham's examples.
You can implement a numeric tower in F# either using a union type (liketype number = Int of int | Float of float) or by boxing everything. The following solution uses the latter approach:
let add (x: obj)(y: obj)= match x, y with |(:? int as m),(:? int as n)-> box(m+n) |(:? int as n),(:? float as x) |(:? float as x),(:? int as n)-> box(x + float n) |(:? float as x),(:? float as y)-> box(x + y) | _ -> failwith "Run-time type error"
let acc x = let x = ref x fun(y: obj)-> x := add !x y !x
let x : obj -> _ = acc(box 1) do x(box 5) do acc(box 3) do printfn "%A"(x(box 2.3))
However, numeric towers are of little use in general purpose programming and usually do more harm than good. Trying to learn from these kinds of challenges can do more harm than good. The real question is: why we do not want a numeric tower, do not want to box and do not want run-time type promotion?
In other words, why didn't we just write:
let x =1 let x = x +5 ignore(3) let x = float x +2.3
We know the type of x at every step. Every number is stored unboxed. And we know that this code cannot produce a run-time type error.