After being used to languages that did conversions up down left and right for me all the time, I too found it a bit odd when starting with F#.

Then I realised how handy it is to not have that kind of magic happen behind your back and it's not as annoying as I first thought. It might be nice if integer literals would select the right type, but I am not sure it'd always be so simple and straightforward to actually implement that.

(As a side note, just yesterday I found a bug in a C# project of ours, having to do exactly with these automatic conversions between numeric types.)

By on 3/11/2009 9:19 AM ()

Well I didn't test the code :) So yea maybe you have to do "when n=GenericZero"

By on 3/11/2009 1:05 PM ()

When I did that, it inferred it as int -> int. Don't member constraints only work on static type variables (^a), which means inlining to stay generic?

By on 3/11/2009 1:49 PM ()

Yea you convinced me to actually type the code and test it. Needs inline. rec inline appears to work fine, but I get strange results. There's no way for me to view, modify, see, print, or do anything with values in FSI. I'm not even sure it's doing anything.

1
2
3
4
5
6
7
8
9
10
11

open Microsoft.FSharp.Core.LanguagePrimitives

let rec inline factorial n =
match n with
| _ when (n=GenericZero) -> printfn "hi!  %d" n; GenericOne
| _ -> printfn "howdy!  %d" n; n * (factorial (n - GenericOne))

let x = factorial 5
printfn "x = %d" x
let y = factorial 7UL
printfn "y = %d" y

Sending this all to FSI at once and it all compiles successfully, but nothing at all is actually printed. Furthermore, I can't even use x and y in FSI, it says they're undefined. Seems a little strange, not sure why this happens.

By on 3/11/2009 2:24 PM ()

Yeah, it looks like my first bit of advice in that other thread won't really work... I'll edit that post to correct it. My second bit of advice there will work, though, which is to make an inlined non-recursive top level function which wraps a local recursive function. In this case that gives you:

1
2
3
4
5
6
7
8
9
10
11
12

open Microsoft.FSharp.Core.LanguagePrimitives
let inline factorial k =
let rec factorial' n =
match n with
| _ when (n=GenericZero) -> printfn "hi!  %d" n; GenericOne
| _ -> printfn "howdy!  %d" n; n * (factorial' (n - GenericOne))
factorial' k
let x = factorial 5
printfn "x = %d" x
let y = factorial 7UL
printfn "y = %d" y


This appears to work correctly, although the type constraints are somewhat ghastly (partly because of the printfn statements).

By on 3/11/2009 3:19 PM ()

Hmm, won't compile for me:

val inline factorial :
^a -> ^c
when ^a : (static member get_Zero : -> ^a) and
^a : (byte|int16|int32|int64|sbyte|uint16|uint32|uint64|nativeint|
unativeint) and ^b : (static member get_One : -> ^b) and
( ^a or ^b) : (static member ( - ) : ^a * ^b -> ^a) and
^c : (static member get_One : -> ^c) and
( ^a or ^c) : (static member ( * ) : ^a * ^c -> ^c)

| _ -> printfn "howdy! %d" n; n * (factorial (n - GenericOne))
---------------------------------------^^^^^^^^^^

stdin(11,40): error FS0191: The value 'FSI_0003.factorial' was marked inline but was not bound in the optimization environment.

By on 3/11/2009 3:16 PM ()

It is by design. F# is very explicit; implicit conversions can interact badly with type inference, overloading, and other features.

By on 3/11/2009 9:07 AM ()

I asked the same question a few days ago in a different context. It's definitely by design, but what the other person responded to me the other day is that you can get around it by forcing the compiler to be more generic.

1
2
3
4
5
6

open Microsoft.FSharp.Core.LanguagePrimitives

let rec factorialUInt n =
match n with
| GenericOne -> n
| _ -> n * factorialUInt (n - GenericOne)

Now it should work for both ints and int64s. Just as a nitpick though, you might want to change it to

1
2
3
4
5
6
7
8
9
10
11
12

let rec factorialUInt n =

match n with

| GenericZero -> n

| _ -> n * factorialUInt (n - GenericOne)



since 0! is well-defined.

As for why it's by design, someone else can answer that better than I can :)

By on 3/11/2009 9:04 AM ()

But GenericZero is not a literal, so using it in a pattern means you're binding a value to that identifier. You'd have to use "| n when n = GenericZero", no?

I thought you needed inline to make it generic across integer types. And I don't think rec inline works?

By on 3/11/2009 9:15 AM ()

10 years later ;).

>> And I don't think rec inline works?

It appears to give problems, yes. See, for instance [link:github.com] And these have not been resolved in the 10 years since.

While I believe that technically this could be made workable, provided the code is tail recursive, it is probably too hard a challenge to attempt at the moment. But F# is open source, who knows someone picks it up.

A workaround with the factorial code is to use an inner let rec and an outer let inline, which will resolve the issue (see also discussion in the linked thread).

By on 2/4/2019 9:10 AM ()