Как определить пользовательский оператор в вычислительном выражении
Я хочу определить некоторые пользовательские операторы в моем вычислительном выражении, но не могу заставить его работать
type ZipSeq() =
[<CustomOperation("<*>")>]
member this.Apply f s =
f |> Seq.zip s |> Seq.map (fun (y, x) -> x(y))
member this.Return x =
Seq.initInfinite (fun _ -> x)
// (a -> b) -> seq<a> -> seq<b>
[<CustomOperation("<!>")>]
member this.Map f s =
this.Apply (this.Return f) s
let zipSeq = new ZipSeq()
let f (a : float) = a * a
let s = seq { yield 1. }
// seq<b>
let h1 = zipSeq.Map f s
//thinking h1 should be the same as h2
//but compilation error : ` This value is not a function and cannot be applied`
let h2 = zipSeq { return f <!> s }
Кстати, меняется member this.Map f s ...
в member this.Map (f, s) ...
выдает ту же ошибку.
1 ответ
Как уже упоминалось в комментариях, вычислительные выражения и пользовательские операторы - это две функции ортогонального языка, которые никак не взаимодействуют. Если вы хотите использовать пользовательские операторы, вы можете просто определить пользовательский оператор и использовать его (вы можете определить их как члены типа для ограничения их области действия или как члены модуля, который должен быть явно открыт).
Если вы заинтересованы в использовании выражений вычислений для чего-то вроде аппликативного стиля программирования, стоит отметить, что вы можете определять "zip-подобные" операции в выражениях вычислений. Это позволяет вам писать молнии с хорошим синтаксисом:
zipSeq {
for x in [1; 2; 3] do
zip y in ['a'; 'b'; 'c']
yield x, y }
Это производит последовательность с [1,a; 2,b; 3,c]
, Определение построителя вычислений, которое позволяет вам сделать это, выглядит следующим образом:
type SeqZipBuilder() =
member x.For(ev:seq<'T>, loop:('T -> #seq<'U>)) : seq<'U> =
Seq.collect loop ev
member x.Yield(v:'T) : seq<'T> = seq [v]
[<CustomOperation("zip",IsLikeZip=true)>]
member x.Zip
( outerSource:seq<'Outer>, innerSource:seq<'Inner>,
resultSelector:('Outer -> 'Inner -> 'Result)) : seq<'Result> =
Seq.map2 resultSelector outerSource innerSource
let zipSeq = SeqZipBuilder()
(Насколько я знаю, это не очень хорошо задокументировано, но в репозиториях F# есть множество примеров, которые показывают, как можно определить zip-подобные (и другие) пользовательские операции.)