I think there was a semantic change here.

1
2
3
4
5
6
7
 

[for Some nm in [ Some("James"); None; Some("John") ] do
    yield nm.Length ]
|> Seq.iter (printfn "%A")

At some point in the past, I think this would print 5 and 4. Now it raises an exception. The 'pattern' of a 'for' used to apply an implicit filter (simply dropping unmatched values), but now it works like any other pattern (raising MatchFailureException). I don't recall offhand when that change was made.

By on 10/24/2009 12:24 PM ()

The strange thing is that implicitly dropping unmatched values does work in a regular for loop:

1
2
3
4
5
6
7
8
> for _::_ as L in [[1;2;3];[];[4;5;6]] do printfn "%A" L;;

[1; 2; 3]

[4; 5; 6]

val it : unit = ()

This works, whereas

1
2
3
4
5
6
7
8
> [for _::_ as L in [[1;2;3];[];[4;5;6]] do yield L];;

  [for _::_ as L in [[1;2;3];[];[4;5;6]] do yield L];;

  ------------------^^^^^^^^^^^^^^^^^^^^

stdin(2,19): warning FS0025: Incomplete pattern matches on this expression. ...

doesn't, as this thread shows.

Is there any reason why this behavior is different between regular for loops and for loops inside a list (sequence) comprehension?

I like the implicit ignore.

By on 10/25/2009 1:36 AM ()

Ah yes, I think I remember better now; the inconsistency was only brought to light at the end of Beta2, at which point we didn't want to make a breaking change either way. Though the 'implicit ignore' was kept in the for-loop-statement context (but not the for-comprehension case, or any other pattern-matching case elsewhere in the language), in the final release both versions will warn about the incomplete match. (The new warning will effectively discourage utilizing the 'implicit ignore' feature, so as to make it less likely for people changing their code to/from comprehensions to unexpectedly experience a behavior change due to the inconsistency in the language spec here.)

By on 10/25/2009 5:06 AM ()

From what I can tell any pattern matching on an enumerable that doesn't match every element will result in an error. Also, the compiler still emits the "incomplete pattern matches" expression when there is an explicit filter.

1
2
3
  seq { for Some nm in [Some("James"); None; Some("John")] |> List.filter (fun s -> s.get_IsSome()) -> nm.Length};;

stdin(346,22): warning FS0025: Incomplete pattern matches on this expression. For example, the value 'None' may indicate a case not covered by the pattern(s).

A compiler warning like this seems to be indicating that there's a more idiomatic way to accomplish the goal. To make the goal explicit let's say that given:

1
'a option list

We want to create:

1
'b seq

Where all None values from the original list are removed. Below I have my naive, verbose attempt at doing this.

1
seq { for nm in [Some("James"); None; Some("John")] |> List.filter (fun s -> s.get_IsSome()) |> List.map (fun s -> s.get_Value()) -> nm.Length }

Compared to the no longer valid original in the Expert F# book:

1
seq { for Some nm in [Some("James"); None; Some("John")] -> nm.Length }

Given what has shipped, I accept that there are very good reasons for effectively removing the implicit ignore pattern matching. Couldn't we have a way to tell the compiler we want the implicit ignore? What about using the flexible type operator?

1
seq { for #Some nm in [Some("James"); None; Some("John")] -> nm.Length }

If there is a nice idiomatic way of doing this please let me know.

ca

By on 8/5/2010 8:53 AM ()

Seq.choose is your friend:

1
2
3
4
5
6
7
8
9
10
let l = [Some("James"); None; Some("John")]
 
let r = 
    seq { 
        for nm in l |> List.filter (fun s -> s.get_IsSome()) |> List.map (fun s -> s.get_Value()) do
            yield nm.Length } 
printfn "%A" r
 
let r2 = l |> Seq.choose id |> Seq.map (fun s -> s.Length)
printfn "%A" r
By on 8/5/2010 9:22 AM ()

Thanks, that's a lot better; is there a similar an idiom for the general case of going from a #seq of a discriminated union to a seq of a discriminator?

It feels as if something particularly elegant has been lost.

seq { for Some nm in names -> nm.Length }

In other languages I would have used a guard clause in the comprehension. Here the discriminator worked in the pattern to the same end, simultaneously more concise and understandable. To me, this reads less like an "implicit ignore" and more like an explicit filter.

By on 8/5/2010 12:08 PM ()

Alternatively:

1
let r3 = l |> Seq.choose (Option.map (fun s -> s.Length))
By on 8/5/2010 9:39 AM ()

It's great to hear that in the final release for loops both inside and outside a sequence comprehension will have the same behavior.

I also agree that any time values could be silently discarded the user needs to be warned about it.

By on 10/25/2009 5:55 AM ()

Hi,

This short syntax was removed, the recommended way is to use the more explicit syntax. This should work:

1
2
[for i in [ Some("James"); None; Some("John") ] do
  match i with Some nm -> yield nm.Length | _ -> ()]
By on 10/24/2009 10:55 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper