I'm trying to define a function, factorize, which uses structural type constraints (requires static members Zero, One, +, and /) similar to Seq.sum so that it can be used with int, long, bigint, etc. I can't seem to get the syntax right, and can't find a lot of resources on the subject. This is what I have, please help.
let inline factorize (n:^NUM) =
^NUM : (static member get_Zero: unit->(^NUM))
^NUM : (static member get_One: unit->(^NUM))
let rec factorize (n:^NUM) (j:^NUM) (flist: ^NUM list) =
if n = ^NUM.One then flist
elif n % j = ^NUM.Zero then factorize (n/j) (^NUM.One + ^NUM.One) (j::flist)
else factorize n (j + ^NUM.One) (flist)
factorize n (^NUM.One + ^NUM.One) []
Inspired by kvb's answer using NumericLiterals, I was driven to develop an approach which would allow us to force "sane" type signatures without having to add extensive type annotations.
First we define some helper functions and wrapper type for language primitives:
Then we have:
[Edit: due to an apparent bug with F# 2.0 / .NET 2.0, factorizen, factorizeL, and factorizeI below run significantly slower than factorizeG when compiled in Release-mode but otherwise run slightly faster as expected -- see F# performance question: what is the compiler doing?]
Or we can take it a few step further (inspired by Expert F#, p.110):
Also, here is an extended version of kvb's NumericLiteralG which allows us to use "2G", "-8G", etc. Though I couldn't figure out how to implement a memoization strategy (though that should be doable for G.any).
Firstly, here is a trivial example that shows how the syntax should look like:
In some cases, you don't need to write the constraints explicitly (the F# compiler will actually warn you about that if you write the above), because some static members are well-known to the compiler and there are standard functions for using them. So, you can use the function and the compiler will infer the constraint:
Unfortunately, this really doesn't help you, because recursive functions cannot be declared as
inline
(for obvious reasons - the compiler cannot inline the function at compile time, because it doesn't know how many times), so static constraints are probably not powerful enough for your problem.[EDIT: This is actually possible for some functions (see kvb's answer)]
I think you'll need
NumericAssociations
instead, which were alreaday discussed in this question (these are processed at runtime, so they are slower - but are used to implement for example F# matrix type - the matrix can cache the dynamically obtained information, so it is reasonably efficient).Here's how I'd write it:
The type inferred for factorize here is way too general, but the function will work as you'd expect. You can force a more sane signature and set of constraints if you want by adding explicit types to some of the generic expressions: