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.
(->) 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.
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.
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.
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.
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)
id
, fst
, snd
, flip
, tuple2
, uncurry
etc. as neededAnyway 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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With