I'm trying to do something that would be easy if I could use type classes in F#.
I want to be able to calculate a running average on an arbitrary type upon which I have defined addition, scalar multiplication and scalar division. This is my non-compiling attempt so far:
let updateMean<'a when 'a : (static member (*) : 'a -> float32 -> 'a) and 'a : (static member (/) : 'a -> float32 -> 'a) and 'a : (static member (+) : 'a -> 'a -> 'a)> (newObservation : 'a) (currentMean : 'a) (currentNumberOfRecords : int) =
(newObservation + (currentMean * (currentNumberOfRecords |> float32))) / float32 (1 + currentNumberOfRecords)
and this is the confusing error message:
A type parameter is missing a constraint 'when ( ^a or ^?766914) : (static member ( + ) : ^a * ^?766914 -> ^?766915)'
You can get a bit further if you add inline
(which is a requirement for using static member constraints) and rename the static variable from 'a
to ^a
(which is a syntax used by statically resolved type parameters) and remove the explicit specification of constraints. The compiler will then attempt to infer the type of the constraints based on the code (which makes it a bit more usable):
let inline updateMean (newObservation : ^a) (currentMean : ^a)
(currentNumberOfRecords : int) =
(newObservation + (currentMean * (currentNumberOfRecords |> float32))) /
(float32 (1 + currentNumberOfRecords))
However, this still does not work, because you are restricting currentMean
to float32
- in general, the compiler requires that both of the parameters of an operator have the same type. You can keep the currentNumberOfRecords
as a value of the same type - then the only tricky part is adding one, which can be done using LanguagePrimitives.GenericOne
:
let inline updateMean newObservation currentMean currentNumberOfRecords =
(newObservation + (currentMean * currentNumberOfRecords)) /
(LanguagePrimitives.GenericOne + currentNumberOfRecords)
This works nicely, but I would probably use a slightly different approach and keep the total sum together with the total count (and then just divide them to get the mean - this will probably have better numerical properties as you avoid repeated rounding):
let inline updateState (currentSum, currentCount) newObservation =
(currentSum + newObservation, currentCount + 1)
let inline currentMean (currentSum, currentCount) =
LanguagePrimitives.DivideByInt currentSum currentCount
The trick is to use operations from the LanguagePrimitives module and let the F# compiler figure out the type constraints automatically (because they are pretty ugly).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With