Выражения вычислений F# и оператор возврата

На веб-сайте Try F# они приводят пример вычислительного выражения:

type Age =
| PossiblyAlive of int
| NotAlive

type AgeBuilder() =
    member this.Bind(x, f) =
        match x with
        | PossiblyAlive(x) when x >= 0 && x <= 120 -> f(x)
        | _ -> NotAlive
    member this.Delay(f) = f()
    member this.Return(x) = PossiblyAlive x

let age = new AgeBuilder()

let willBeThere (a:int) (y:int) =
  age { 
    let! current = PossiblyAlive a
    let! future = PossiblyAlive (current + y)

    return future
  }

что немного похоже на стандартную монаду Maybe в Haskell.

Однако в истинной форме Haskell я бы хотел использовать return для двух строк:

let! current = PossiblyAlive a
let! future = PossiblyAlive (current + y)

to be:

let! current = return a
let! future = return (current + y)

однако это не работает. Самое близкое, что я получаю, это:

let! current = age.Return a
let! future = age.Return (current + y)

но это выглядит грязно. Есть ли способ использовать return без явного использования функции построения вычислений?


person Tahir Hassan    schedule 08.07.2014    source источник
comment
Ваш код работает для меня - какую ошибку вы получаете?   -  person Lee    schedule 08.07.2014
comment
нет ошибки - речь идет о синтаксисе выражения вычисления и операторе возврата.   -  person Tahir Hassan    schedule 08.07.2014


Ответы (2)


Вы можете создать вложенное выражение:

let! current = age { return a }
let! future = age { return (current + y) }

хотя вы могли бы просто использовать let вместо этого:

let current = a
let future = current + y

Имейте в виду, что этот строитель нарушает законы монад, поскольку

return 150 >>= return не то же самое, что return 150

person Lee    schedule 08.07.2014
comment
Отлично ... ответил на мой вопрос, а также подтвердил мое подозрение, что было что-то сомнительное в определении возврата, не выполняющем проверку границ. - person Tahir Hassan; 08.07.2014

Я рассмотрел эту проблему более подробно и думаю, что нашел разумную альтернативу использованию синтаксиса age { return <expr> }, который Ли показывает в своем ответе.

Моя главная претензия к этому синтаксису заключается в том, что мы уже находимся в монаде age, поэтому любой оператор return в теле должен автоматически разрешаться в age.Return. Однако исправление этого, вероятно, не имеет большого значения для команды разработчиков F#, поскольку обходной путь очень прост.

Моя альтернатива состоит в том, чтобы перегрузить метод Bind функцией, которая принимает значение, которое затем поднимается; затем отправляет это поднятое значение другой функции Bind:

type Age =
| PossiblyAlive of int
| NotAlive

type AgeBuilder() =
    let reasonableAge (x:int) = x >= 0 && x <= 120

    member __.Return x = 
        if reasonableAge x then PossiblyAlive x else NotAlive

    member __.Bind(x:Age, f) =
        match x with
        | PossiblyAlive x when reasonableAge x -> f x
        | _ -> NotAlive

    member this.Bind(x:int, f) =
        this.Bind(this.Return(x), f)

let age = new AgeBuilder()

let willBeThere (a:int) (y:int) =
    age { 
        let! current = a
        let! future = (current + y)
        return future
    }
person Tahir Hassan    schedule 01.08.2014