3

According to the MSDN documentation on the for ... to loop in F#:

The type of the identifier is inferred from the type of the start and finish expressions. Types for these expressions must be 32-bit integers.

But, with the code below, I get the following compile-time error:

for bar = 0u to 5u do
    let baz : uint32 = bar
    ()
error FS0001: This expression was expected to have type
    int    
but here has type
    uint32

If I put the loop inside a sequence, though, it compiles without error:

let foo =
    seq {
        for bar = 0u to 5u do
            let baz : uint32 = bar
            yield baz
    }
val foo : seq<uint32>

What's going on? Why does the for-loop infer uint32 in the second example but not the first?

I have an external library which takes an unsigned 32-bit integer as an index. I need to iterate from 0 to the length of the collection (also uint32) minus one. When I put this logic inside a sequence and yield each item, it compiles without any errors and runs just fine. But when I attempt to read all the items outside a sequence, the compiler bombs. I'm forced to perform type convers from uint32 to int and back again, which, in my opinion, has a very bad smell to it.

JDB
  • 25,172
  • 5
  • 72
  • 123
  • See the comments on the linked question for why it works within a sequence expression. – Daniel Jan 22 '14 at 19:26
  • @Daniel - uint32 is a 32-bit integer. – JDB Jan 22 '14 at 19:26
  • No, it's a 32-bit _unsigned_ integer. `int` is the only 32-bit integer. – Daniel Jan 22 '14 at 19:27
  • @Daniel - OK... so why does it work inside a sequence? – JDB Jan 22 '14 at 19:29
  • As mentioned in the comments on the other question, computation expressions are de-sugared to method calls. The same limitations do not apply. – Daniel Jan 22 '14 at 19:30
  • @Daniel - Ah, just saw your second comment... I don't think it really counts as "type inferrence" if it can only infer a single type. The MSDN documentation is misleading. – JDB Jan 22 '14 at 19:33
  • I'm not sure what part of the docs you're referring to, but even if only a single type is possible, not having to explicitly state it, technically, fits the definition of type inference. – Daniel Jan 22 '14 at 19:35
  • @Daniel - See the quoted documentation in the question. – JDB Jan 22 '14 at 19:37
  • The range of types that can be inferred is always limited, in this case it just happens to be limited to a single type. I agree, the wording is a little misleading (although technically accurate). Maybe it's just to emphasize that a type annotation is optional, as it is virtually everywhere else in F#. – Daniel Jan 22 '14 at 19:40
  • @Daniel - a type annotation is explicitly not allowed. It will not compile if provided a type annotation. – JDB Jan 22 '14 at 19:42
  • That seems like a good reason to mention type inference in that context. – Daniel Jan 22 '14 at 19:49
  • @Daniel I believe that it could have been clearer, though. "*The types of the start and finish expressions must be **signed** 32-bit integers, and the type of the identifier is inferred from these expressions.*" Nonetheless, thanks for your help. – JDB Jan 22 '14 at 19:54

1 Answers1

4

As explained in this comment by Daniel,

... Within a computation expression for is desugared to a method call, which isn't inherently imperative like a loop, and therefore doesn't have the same limits. ...

Here's a simple work-around:

for bar in 0u .. 5u do
    let baz : uint32 = bar
    ()
Community
  • 1
  • 1
JDB
  • 25,172
  • 5
  • 72
  • 123