Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Arrow "proc" notation in F#

Is there an implementation of "proc" notation for arrows in F#? In Haskell, it looks like this:

mean2 :: Fractional a => Circuit a a
mean2 = proc value -> do
    t <- total -< value
    n <- total -< 1
    returnA -< t / n

Note the proc keyword and -< symbol.

Ideally, this would probably use computation expressions somehow, but I'm open to other approaches as well.

like image 594
Brian Berns Avatar asked Apr 21 '21 18:04

Brian Berns


People also ask

What does -> mean in Haskell?

(->) is often called the "function arrow" or "function type constructor", and while it does have some special syntax, there's not that much special about it. It's essentially an infix type operator. Give it two types, and it gives you the type of functions between those types.

What does arrow do in Haskell?

The Arrow (either (->) or MyArr ) is an abstraction of a computation. For a function b -> c , b is the input and c is the output. For a MyArr b c , b is the input and c is the output.

What does left arrow mean in Haskell?

The left arrow gets used in do notation as something similar to variable binding, in list comprehensions for the same (I'm assuming they are the same, as list comprehensions look like condensed do blocks), and in pattern guards, which have the form (p <- e). All of those constructs bind a new variable.


Video Answer


2 Answers

I haven't seen any equivalent notation to that in F#. I think the reason is that F# doesn't attempt to be pure and therefore doesn't have the problem that proc and -< is solving.

The equivalent F# would just be:

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
    }

Perhaps I have misunderstood and the proc keyword offers a lot more functionality than this simple example.

like image 165
tranquillity Avatar answered Oct 18 '22 05:10

tranquillity


UPDATE throughout this answer:

This is not Proc equivalent notation but you can write in F#:

let mean =
    let t = total << id
    let n = total << List.map (constant 1)
    listFork t (/) n 

I explain this in what follows.

I prefer J's fork conjunction which inspires the fork below - but note it is not quite the same - to do all the work in F# that, as I understand it, that Arrows would do, but this is guided by the Arrow implementation in the above mentioned F# library rather than looking at the Haskell usage (this is separate to Kliesli Arrows, I use those too, you create an infix operator for those). Anyway in 1 line:

let fork g h f a = h (g a) (f a)
  • used in conjunction with id, fst, snd, flip, tuple2, uncurry etc. as needed

Anyway the way I would use fork to create the average as described in the question:

let average values = fork List.sum (/) List.length values

or

let average = fork List.sum (/) List.length 

The running average version, using list here. (fork can be raised to different collections, here it is listFork (if extended to list collection could then be List.fork) so as common to F# unlike Haskell, need dedicated seqFork, arrayFork combinators etc.)

UPDATE: Made as similar in style to Proc as possible:

let total: int list -> float list = List.scan (+) 0 >>List.tail >> List.map float
let constant i = fun _ -> i

let fork g h f a = h (g a) (f a)
let inline listFork g h f  = fork g (List.map2 h) f 

let mean =
    let t = total << id
    let n = total << List.map (constant 1)
    listFork t (/) n 

mean [0;10;7;8];;    

> 
val total : (int list -> float list)
val constant : i:'a -> 'b -> 'a
val fork : g:('a -> 'b) -> h:('b -> 'c -> 'd) -> f:('a -> 'c) -> a:'a -> 'd
val inline listFork :
  g:('a -> 'b list) ->
    h:('b -> 'c -> 'd) -> f:('a -> 'c list) -> ('a -> 'd list)
val mean : (int list -> float list)
val it : float list = [0.0; 5.0; 5.666666667; 6.25]

It is not Proc notation obviously. Now I have the running version, I don't know whether this answers your "other approaches as well" finishing point...

Final Update:

With listFork you can make this more point-free with just a 1-liner:

let mean1 = listFork total (/) (List.map (fun _ -> 1) >> total)

What is not clear in the first example linked to in the OP, is that total is itself an arrow which does the heavy lifting of a fold or scan. That is the real power of Arrows hence their usefulness in FRP, but that is (or was) outside the scope of my usage of fork - which was more focused on point-free, composition and piping. At least till now, I will consider this further but not here!

like image 41
Martin Freedman Avatar answered Oct 18 '22 05:10

Martin Freedman