Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement automatic generalization in a simple function

I created the following operator to help with safe division.

let (/!) a b = 
    if b = 0 then 0 
    else a / b

The problem is that it only works with integers, and I'd like this function to work with any numeric primitive (int, float, decimal, etc...).

I've done some reading on automatic generalization, but it's not quite sinking in, and I'm not sure if that's even the right direction to be heading.

How do I accomplish generalizing this operator?

Thanks,

Joe

like image 663
Joe Avatar asked May 15 '26 04:05

Joe


1 Answers

Hi this is a bit of a hidden gem but what you are looking for is this:

let inline (/!) (a : ^a) (b : ^a) : ^a = 
    if b = LanguagePrimitives.GenericZero 
    then LanguagePrimitives.GenericZero 
    else a / b

btw: this has this monster type:

val inline ( /! ) :
  a: ^a -> b: ^a ->  ^a
    when  ^a : equality and  ^a : (static member get_Zero : ->  ^a) and
          ^a : (static member ( / ) :  ^a *  ^a ->  ^a)

(this is why I don't really like to write this in the declaration ;) )

As you can see there is support for generic numeric code, but it's not that often discussed (F# has some type-class like things build in - this is a example, the others are stuff like comparable, etc.)

Tomas wrote a good article about this: Writing generic numeric code

PS you don't need the ^a either - but I kindof like to write the signature - and even if you can do this:

let inline (/!) a b = 
    if b = LanguagePrimitives.GenericZero 
    then LanguagePrimitives.GenericZero 
    else a / b

val inline ( /! ) :
  a: ^a -> b: ^b ->  ^c
    when ( ^a or  ^b) : (static member ( / ) :  ^a *  ^b ->  ^c) and
          ^b : (static member get_Zero : ->  ^b) and  ^b : equality and
          ^c : (static member get_Zero : ->  ^c)

it will do you no good as the real division operators are usually only on one type for both arguments - and as I said: I like to emphazise the types over the argument-names ;)

fun fact

you can get around the GenericZero thing like this:

> let inline (/!) a b = if b = (b-b) then (b-b) else a/b;;

val inline ( /! ) :
  a: ^a -> b: ^b ->  ^b
    when ( ^a or  ^b) : (static member ( / ) :  ^a *  ^b ->  ^b) and
          ^b : (static member ( - ) :  ^b *  ^b ->  ^b) and  ^b : equality

(Just to be safe: you might run into trouble with certain types / numbers here ;) )

like image 50
Random Dev Avatar answered May 18 '26 00:05

Random Dev