Опустите "сделай!" в выражении вычисления

Можно ли собрать компоновщик выражений вычислений, который может выставлять два или более выражения без размещения do! перед каждым?

Если я правильно прочитал соответствующий раздел руководства, это должно быть возможно с помощью метода builder Combine. Однако мой метод Combine не используется; вместо этого я получаю предупреждение о компиляторе, предлагая использовать ignore для отказа от результата.

Например, учитывая F # State monad, я хотел бы иметь возможность сделать это:

let hello who = State (fun lines -> lines @ [sprintf "hello %s" who])
let m = state {
    hello "world"
    hello "F#"
}
let l = Execute m []
// l should now contain ["hello world"; "hello F#"]

Ответ 1

В дополнение к тому, что написал Дарио, вы не можете переопределить обычную последовательность, чтобы вести себя как монадический do! (даже если вы решили не поддерживать немонодические операции в своих вычислениях). Проблема состоит в том, что обычная последовательность не обрабатывается каким-либо особым образом переводчиком, поэтому нет точки перехвата, которую вы могли бы использовать. Ваш пример будет переведен следующим образом:

let m = state.Delay(fun () ->
  hello "world"
  hello "F#"
  state.Zero())

Вызов hello не включается в любой членский вызов, который можно переопределить.

В более ранней версии последовательности F #, используемой для перевода на вызовы члена Let, как это:

let m = state.Delay(fun () ->
  state.Let(hello "world", fun _ ->
    state.Let(hello "F#", fun _ -> 
      state.Zero())))

Таким образом, можно было делать то, что вы хотели, но это невозможно сделать в текущей версии (я полагаю, одна причина заключалась в том, что это добавляет слишком много обертывания, что ухудшает производительность).

Ответ 2

Нет, потому что для этого используется обычный do. Если вы хотите, чтобы monadic делал, т.е. do!, вы должны быть явным. Пример:

let m = state {
    printfn "World"
    hello "World"
    return 42
}
  • printfn выражение имеет тип unit, а do printfn неявно.
  • И в случае hello? Нам понадобится монадическая привязка!

Теперь, как компилятор знает, что выбрать, do или do!? Может быть, по типам (Scala делает это), но прямо сейчас это не так. Поэтому он просто принимает обычный do где угодно.