3

Why int64 doesn't support LanguagePrimitives.DivideByInt? I thought it would be pretty natural to write something like this:

let inline DivBy2 n = LanguagePrimitives.DivideByInt n 2
let res = DivBy2 100L

But compiler says that int64 doesn't support the operator DivideByInt.

I've tried to cheat with:

type System.Int64 with 
    static member DivideByInt (n: System.Int64) (d: int) = n / (int64 d)

But it doesn't work.

What can be done to perform generic division of int64 by int?

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
Dmitrii Lobanov
  • 4,897
  • 1
  • 33
  • 50
  • I think it might be easiest to add this by rebuilding `FSharp.Core` - the code you would need to change is around line 2300 of `prim-types.fs` – John Palmer Mar 02 '12 at 05:23
  • Yeah, I'm wondering why there's no support for int64. I've sent equest to fsbugs to undertand their desigion on this problem. – Dmitrii Lobanov Mar 02 '12 at 17:23

5 Answers5

7

Looking at the F# source code, the type int64 is not included in the function DivideByInt, I don't know why.

You can defining another generic function like this:

open LanguagePrimitives
type DivExtension = DivExtension of int with
    static member inline (=>) (x             , DivExtension y) = DivideByInt x y
    static member        (=>) (x:int64       , DivExtension y) = x / (int64 y)
    static member        (=>) (x:DivExtension, DivExtension y) = x

let inline DivByInt x y = x => DivExtension y

Or you can even shadow the original DivideByInt function:

let inline DivideByInt x y = x => DivExtension y

Note you can also add more overloads (ie for int), in which case you don't need the last "dummy" overload to infer the right signature.

Gus
  • 25,839
  • 2
  • 51
  • 76
  • Why there's a need for the last "dummy overload"? – Dmitrii Lobanov Mar 02 '12 at 17:25
  • 1
    Because if you take it out, type inference will unify both remaining overloads with int64. You can use type annotations with all the when constraints or just add another overload and it will be automatically generalized. – Gus Mar 02 '12 at 17:39
3

I've received an answer from Don Syme (via fsbugs email) when I've asked about missing MutliplyByInt and limited support of DivideByInt:

Don's answer:

This operator exists to support “Seq.averageBy” etc. This represents pseudo-precise division of the total by the count. We didn’t extend the mechanism beyond what was needed for that.

So it looks like I've misunderstood the purpose of this mechanism. But I still don't know why there's no support for int64 for DivideByInt, that means that we're limited in generic operations on int64 type. Perhaps I'm confused because int64 looks like a primitive type when it's not a primitive type. And DivideByInt is defined only for primitive types, that's why there's no support for it.

Dmitrii Lobanov
  • 4,897
  • 1
  • 33
  • 50
2

As you point out, DivideByInt mainly exists to support Seq.averageBy. The fact that integral types aren't supported makes sense to me, since the average of [1;2] ought to be 1.5, not 1 (which is what a DivideByInt implementation would give you). This forces you to convert your inputs to floats, which gives you the expected answer.

kvb
  • 54,864
  • 2
  • 91
  • 133
  • 2
    Oh, your answer forced me to verify that `int` also doesn't support `DivideByInt`. The original question was about why `int64` isn't supported when `int` is supported. And it turns out that no single integral type is supported. Thanks! Now it realy makes sense, it just puts everything on their places! – Dmitrii Lobanov Mar 08 '12 at 16:40
1

Can you define it this way?

let inline DivBy2 n = 
  let one = LanguagePrimitives.GenericOne
  n / (one + one)
Daniel
  • 47,404
  • 11
  • 101
  • 179
  • Oh, I've posted an example with DivBy2 just as an example. I'm wondering whether it is possible in F# to use int64 where there's `DivideByInt` constraint. – Dmitrii Lobanov Mar 02 '12 at 05:55
0

I'm obviously late to the party here, but it would be interesting to know why you want to create a divBy2 function. There are two problems here, and solving one of them might be enough, depending on your needs.

The first problem is that there's no LanguagePrimitives.GenericTwo. That's easy to fix, but the solution is of limited use if you want to define specific division functions for divisors other than 2:

let inline divBy2 n =
    n / (LanguagePrimitives.GenericOne + LanguagePrimitives.GenericOne)

For less typing, you can assign LanguagePrimitives.GenericOne to a variable, which becomes more useful as the magnitude of your divisor increases:

let inline divBy4 n =
    let one = LanguagePrimitives.GenericOne
    let two = one + one
    n / (two + two)

This solution is also unhelpful if you want to create a general function. The "custom" way would be

let inline divBy divisor dividend = dividend / divisor

We can use this with partial function application to halve a list of bytes, for example, like this:

let halfBytes = [ 1uy .. 10uy ] |> List.map (divBy 2uy)

But we can do better. This problem applies to all non-commutative operators, including subtraction. To get around it, we can define

let flip f a b = f b a

This allows, for example

let scaledInThousands = [ 0m .. 500m .. 3000m ] |> List.map (flip (/) 1000m)
let decrementedIntegers = [ 1 .. 10 ] |> List.map (flip (-) 1)

If you want, you can still define the divBy function:

let inline divBy n = flip (/) n
let halfInts = [ 1 .. 10 ] |> List.map (divBy 2)
let halfLongs = [ 1L .. 10L ] |> List.map (divBy 2L)
let fifthLongs = [ 1L .. 10L ] |> List.map (divBy 5L)
let oneThroughTenOverPi = [ 1.0 .. 10.0 ] |> List.map (divBy System.Math.PI)
phoog
  • 42,068
  • 6
  • 79
  • 117