Imagine the following code:
let d = dict [1, "one"; 2, "two" ]
let CollectionHasValidItems keys =
try
let values = keys |> List.map (fun k -> d.Item k)
true
with
| :? KeyNotFoundException -> false
Now let us test it:
let keys1 = [ 1 ; 2 ]
let keys2 = [ 1 ; 2; 3 ]
let result1 = CollectionHasValidItems keys1 // true
let result2 = CollectionHasValidItems keys2 // false
This works as I would expect. But if we change List to Seq in the function, we get different behavior:
let keys1 = seq { 1 .. 2 }
let keys2 = seq { 1 .. 3 }
let result1 = CollectionHasValidItems keys1 // true
let result2 = CollectionHasValidItems keys2 // true
Here with keys2 I can see the exception message within values object in the debugger but no exception is thrown...
Why is it like this? I need some similar logic in my app and would prefer to work with sequences.
This is a classic example of a problem with side effects and lazy evaluation. Seq
functions such as Seq.map
are lazily evaluated, that means that the result of Seq.map
will not be computed until the returned sequence is enumerated. In your example, this never occurs because you never do anything with values
.
If you force the evaluation of the sequence by generating a concrete collection, like a list
, you will get your exception and the function will return false
:
let CollectionHasValidItems keys =
try
let values = keys |> Seq.map (fun k -> d.Item k) |> Seq.toList
true
with
| :? System.Collections.Generic.KeyNotFoundException -> false
As you've noticed, using List.map
instead of Seq.map
also resolves your issue because it will be eagerly evaluated when called, returning a new concrete list
.
The key takeaway is, you have to be really careful about combining side effects with lazy evaluation. You can't rely on effects happening in the order that you initially expect.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.