Обозначение стрелки «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]
Другие вопросы по тегам