F# - Почему Seq.map не распространяет исключения?

Представьте себе следующий код:

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

Теперь давайте проверим это:

let keys1 = [ 1 ; 2 ]
let keys2 = [ 1 ; 2; 3 ]

let result1 = CollectionHasValidItems keys1 // true
let result2 = CollectionHasValidItems keys2 // false

Это работает, как я ожидал. Но если мы изменим List на Seq в функции, мы получим другое поведение:

let keys1 = seq { 1 .. 2 } 
let keys2 = seq { 1 .. 3 }

let result1 = CollectionHasValidItems keys1 // true
let result2 = CollectionHasValidItems keys2 // true

Здесь с keys2 я вижу сообщение об исключении в объекте значений в отладчике, но исключение не выдается...

Почему это так? Мне нужна похожая логика в моем приложении, и я бы предпочел работать с последовательностями.

1 ответ

Решение

Это классический пример проблемы с побочными эффектами и ленивой оценкой. Seq такие функции, как Seq.map лениво оцениваются, это означает, что результат Seq.map не будет вычисляться до тех пор, пока не будет перечислена возвращаемая последовательность. В вашем примере это никогда не происходит, потому что вы никогда ничего не делаете с values,

Если вы форсируете оценку последовательности, генерируя конкретную коллекцию, например list, вы получите исключение и функция вернет false:

let CollectionHasValidItems keys =
    try
        let values = keys |> Seq.map (fun k -> d.Item k) |> Seq.toList
        true
    with
        | :? System.Collections.Generic.KeyNotFoundException -> false

Как вы заметили, используя List.map вместо Seq.map также решает вашу проблему, потому что она будет жадно оценена при вызове, возвращая новый конкретный list,

Ключевой вывод заключается в том, что вы должны быть очень осторожны, сочетая побочные эффекты с ленивой оценкой. Вы не можете полагаться на эффекты, происходящие в том порядке, который вы изначально ожидали.

Другие вопросы по тегам