Soldalma
Soldalma

Reputation: 4758

Why does bigint compile in one case and not in the other?

I am just starting with F#, and I found the behavior displayed below puzzling. Any explanations?

let bg = bigint 1;;
(* val bg : System.Numerics.BigInteger = 1 *)

let bg = (bigint) 1;;
(* error FS0041: A unique overload for method 'BigInteger' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: System.Numerics.BigInteger(value: byte []) : unit, System.Numerics.BigInteger(value: decimal) : unit, System.Numerics.BigInteger(value: float) : unit, System.Numerics.BigInteger(value: float32) : unit, System.Numerics.BigInteger(value: int) : unit, System.Numerics.BigInteger(value: int64) : unit, System.Numerics.BigInteger(value: uint32) : unit, System.Numerics.BigInteger(value: uint64) : unit *)

Upvotes: 4

Views: 110

Answers (1)

Fyodor Soikin
Fyodor Soikin

Reputation: 80805

That's because of overloading. See, bigint is actually a synonym for the BigInteger class, and applying it like a function translates into calling a constructor, but which one of the several constructors depends on the type of the argument.

This works fine when you're applying it like a function: the type of the argument is known, and the compiler can select the appropriate overload. But if you're trying to treat it as a function-value, the argument is absent, and the compiler doesn't know which overload to take.

Here's a simpler repro:

type T() =
  member static f (s: string) = s
  member static f (i: int) = I

let a = T.f 1 // works
let b = T.f "abc" // works
let c = T.f // doesn't work: which `f` do you mean?

// possible workaround: create non-overloaded wrappers
let fStr (s: string) = T.f s
let fInt (i: int) = T.f i
let a = fStr // works fine
let b = fInt // works fine, too

Conclusion: overloading is bad, don't use it. But if you're stuck using it, just stuck it up. Or use a workaround.


On the role of parentheses

(in response to comment)

The parentheses tell the compiler: "first thing, calculate this expression inside the parentheses, then take the result and do the rest with it". So that putting a thing in parentheses is semantically equivalent to giving it a name, e.g.:

// Original repro:
let a = (bigint) 5

// Same thing:
let f = bigint
let a = f 5

Or another way to phrase the same idea: because the function argument is not present inside the parentheses, it cannot be used to evaluate the expression inside the parentheses; and because there is no argument, there is no way to resolve the overload.

Upvotes: 6

Related Questions