Обозначение стрелки «proc» в F#
Есть ли реализация обозначения «proc» для стрелок в F #? В Haskell это выглядит так:
mean2 :: Fractional a => Circuit a a
mean2 = proc value -> do
t <- total -< value
n <- total -< 1
returnA -< t / n
Обратите внимание
proc
ключевое слово и
-<
символ.
В идеале это, вероятно, каким-то образом могло бы использовать вычислительные выражения, но я открыт и для других подходов.
3 ответа
В библиотеке FSharpPlus есть решение, реализующее Arrows.
К сожалению, я недостаточно знаком с Haskell, чтобы переводить ваш код.
(Я предпочитаю вдохновлять J-fork конъюнкции - это не совсем то же самое - делать всю работу в F#, которую, насколько я понимаю, будет делать Arrows, но это руководствуется реализацией Arrow в вышеупомянутой библиотеке F#, а не глядя на использование Haskell (это отдельно от Kliesli Arrows, я тоже использую их, вы создаете для них инфиксный оператор). В любом случае в 1 строке:
let fork h g f a = h (g a) (f a)
- используется вместе с
id
,fst
,snd
,flip
,tuple2
и т. д. по мере необходимости
Есть еще много чего добавить, мне нужно написать об этом в блоге!)
Я не видел эквивалентных обозначений в F#. Я думаю, причина в том, что F # не пытается быть чистым и, следовательно, не имеет проблемы, которая и
-<
решает.
Эквивалентный F # будет просто:
let mean2 input =
let mutable total = 0.
let mutable count = 0.
seq {
for i in input do
total <- total + i
count <- count + 1.
yield total / count
}
Возможно, я неправильно понял, и
proc
ключевое слово предлагает гораздо больше функций, чем этот простой пример.
Вы действительно можете использовать параметризованное вычислительное выражение, которое идет в направлении стрелок. Давайте сначала посмотрим на основные строительные блоки, которые используются для такого рода проблем (которые аналогично доступны для FRP):
-
для захвата состояния при применении папки к последовательности -
рекомбинировать две последовательности
Наивная реализация, как показано ниже, будет повторять входную последовательность дважды, поэтому, возможно, придется подумать над ее кешированием.
let inline total source =
Seq.scan (+) LanguagePrimitives.GenericZero source
|> Seq.skip 1
let inline mean source =
source
|> Seq.map (fun _ -> LanguagePrimitives.GenericOne)
|> total
|> Seq.zip (total source)
|> Seq.map ((<||) (/))
Изменяя
let inline mapTotal (f : 'a->'b) source : seq<'b> =
Seq.scan (fun s t -> f t + s) LanguagePrimitives.GenericZero source
|> Seq.skip 1
type ZipMap<'a>(xs : seq<'a>) =
member __.Bind(a, f) = f (a xs)
member __.Return(op, x, y) = Seq.zip x y |> Seq.map ((<||) op)
let inline mean2 source =
ZipMap<_>(source){
let! t = mapTotal id
let! n = mapTotal (fun _ -> LanguagePrimitives.GenericOne)
return (/), t, n }
mean2 [0.; 10.; 7.; 8.] // seq [0.0; 5.0; 5.666666667; 6.25]
mean2 [0; 10; 7; 8] // seq [0; 5; 5; 6]