Hide this comment

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 ()Reply
Hide this comment

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 ()Reply
Hide this comment

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 ()Reply
Hide this comment

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 ()Reply
Hide this comment

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 ()Reply
Hide this comment

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 ()Reply
Hide this comment

Alternatively:

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

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 ()Reply
Hide this comment

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 ()Reply
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

Logging in...