opened
VesaKarvonen
username_0
I just started a branch

  https://github.com/Hopac/Hopac/tree/sig-rethink

to redesign the symbolic operators and some other parts of the signature of Hopac.  Take a look at the first commit:

  https://github.com/Hopac/Hopac/commit/d47ebbe094d15fe4c98527e33f07e034fda55bb9

The main change to the operator symbols is to change them so that their precedences become "correct" so that parentheses can be eliminated from most common expression involving message passing and/or alternatives.

Here is a kind of visual template of how the common alternative expression can be formed:

```fsharp
    xCh       ^-> fun x -> y         // Sync on take from channel, continue with fun.
<|> xCh *<- x ^->. y                 // Sync on give to channel, return value as result.
<|> xAlt      ^=> fun x -> result y  // Sync on alternative, continue with job.
<|> uAlt      ^=>. yJ                // Sync on alternative, continue with job ignoring result.
```

Also added are new operators `*<-=>` and `*<+->` that directly implement the most common usage patterns (using guard (or prepare) and withNack) for encapsulating asynchronous requests as selective synchronous operations.

```fsharp
qCh *<-=> fun rI -> (q, rI)      // Query with reply var, sync on give to server, wait reply.
qCh *<+-> fun rI n -> (q, rI, n) // Query with reply ch and nack, send to server, sync on reply.
```

These can, of course, also be used with the other alternative operators.  For example, swap channel implementation:

```fsharp
let swap (SwapCh sCh) (msgOut: 'a) =
      sCh ^=> fun (msgIn, outI) -> outI *<= msgOut >>% msgIn
  <|> sCh *<-=> fun inI -> (msgOut, inI)
```

The new message passing operators now have higher precedence than monadic sequencing combinators, so one can now write sequences of message passes without parentheses.  For example:

```fsharp
xI *<= x  >>.
xCh *<- x >>.
yCh *<+ y
```

The available operator symbols in F# are rather limited, but I feel that the redesigned operators lead to slightly cleaner expressions.

There are also some unnecessary operations being removed and I also plan to rename/rething some of the alternative combinators, but more on that later.

Feedback would be welcome!

If you have plenty of extra time, you could try the brach locally and try to format your code with the changes to see if things improve and report back if things get difficult or you don't see a nice way to format your code.
Signature rethink
issue
created
vasily-kirichenko
username_1
The less parenthesis the better :)

From first impression, `*<+` does not look very like an arrow.
comment
created
vasily-kirichenko
username_1
I think removing parenthesis from here

```fsharp
if 0 < n then (dec >>%? n - 1) <|>? (inc >>%? n + 1)
```

```fsharp
if 0 < n then dec ^->. n - 1 <|> inc ^->. n + 1
```

makes the expression harder to read.
comment
created
vasily-kirichenko
username_1
`^->` instead of `|>>?` looks better.
comment
created
vasily-kirichenko
username_1
```fsharp
(source <~> failedMessages) ^=> fun msg ->
```
requires parenthesis (not that I use `<~>` very often :)
comment
created
vasily-kirichenko
username_1
```fsharp
let private takeOrThrow ch = 
    (ch <|> timeOutMillis 10000 ^-> fun _ -> failwith "timeout") |> run 
```
and
```fsharp
let private takeOrThrow ch = 
    ch <|> timeOutMillis 10000 ^-> fun _ -> failwith "timeout" |> run 
```
have completely different semantics. 

i.e. `a1 <|> a2 <|> a3 |> run` is a dangerous expression.
comment
created
vasily-kirichenko
username_1
Switched to Hopac.Extras, see https://github.com/Hopac/Hopac.Extras/commit/b38b8da830d3e995a41595e70916f42fb2ec07ed 

I'm not sure things become much better or worse. I like the consistency of `-` and `=` semantics where `-` always related to functions and `=` - to jobs.
comment
created
vasily-kirichenko
username_1
About `*<+->` and `*<-=>`, they are too complicated for my taste. Hard to remember. However, if split them to `*<+` + `->` and `*<-` + `=>`, then them begin to make sense, though.
comment
created
VesaKarvonen
username_0
That is exactly the idea behind those operators. :)  The left hand side part is the message passing operation used to send the query to the server and right hand side is the message passing operation used to receive the reply from the server.

One way to make this clearer might be to add something between the parts.  To make obvious the idea that you already figured out, the symbols could be `*<-+/-->` and `*<--/=->`.  However, this perhaps gets a bit too long?

```fsharp
let swap (SwapCh sCh) (msgOut: 'a) =
      sCh ^=> fun (msgIn, outI) -> outI *<-= msgOut >>% msgIn
  <|> sCh *<--/=-> fun inI -> (msgOut, inI)
```
vs
```fsharp
let swap (SwapCh sCh) (msgOut: 'a) =
      sCh ^=> fun (msgIn, outI) -> outI *<-= msgOut >>% msgIn
  <|> sCh *<-=> fun inI -> (msgOut, inI)
```

Not sure which I would prefer here.  Which one do you prefer?  Other suggestions are also welcome.

In the object pool, for example, you have:
```fsharp
    let get() = Alt.withNackJob <| fun nack ->
        let replyCh = Ch()
        reqCh *<+ (nack, replyCh) >>% replyCh
```
which could now be written as a one liner:
```fsharp
    let get() = reqCh *<-+/--> fun replyCh nack -> (nack, replyCh)
```
or if you swap that tuple elements, you could write:
```fsharp
    let get() = reqCh *<-+/--> curry id
```
comment
created
VesaKarvonen
username_0
Speaking about more consistent choice of symbols, I wonder whether changing to the same scheme of using `=` for bind-like and and `-` for map-like operators and `.` as "ignore" and `!` as raise.  So, instead of

```fsharp
val (>>=): Job<'x> -> ('x -> Job<'y>) -> Job<'y>
val (>>.): Job<'x> ->        Job<'y>  -> Job<'y>
val (|>>): Job<'x> -> ('x ->     'y ) -> Job<'y>
val (>>%): Job<'x> ->            'y   -> Job<'y>
val (>>!): Job<'x> ->            exn  -> Job<'y>

val (>=>): ('x -> Job<'y>) -> ('y -> Job<'z>) -> 'x -> Job<'z>
```

there would be

```fsharp
val (>>=):  Job<'x> -> ('x -> Job<'y>) -> Job<'y>
val (>>=.): Job<'x> ->        Job<'y>  -> Job<'y>
val (>>-):  Job<'x> -> ('x ->     'y ) -> Job<'y>
val (>>-.): Job<'x> ->            'y   -> Job<'y>
val (>>-!): Job<'x> ->            exn  -> Job<'y>

val (>=>):  ('x -> Job<'y>) -> ('y -> Job<'z>) -> 'x -> Job<'z>
val (>=>.): ('x -> Job<'y>) ->        Job<'z>  -> 'x -> Job<'z>
val (>->):  ('x -> Job<'y>) -> ('y ->     'z ) -> 'x -> Job<'z>
val (>->.): ('x -> Job<'y>) ->            'z   -> 'x -> Job<'z>
val (>->!): ('x -> Job<'y>) ->            exn  -> 'x -> Job<'z>
```

I believe the odd-case of `.>>` is not frequently used with jobs/alts/asyncs and it might be better to just remove it.  It is more useful with parser combinators.

Opinions?

Personally I think that `>>=.` looks a bit odd as I'm used to `>>.`, but I suppose it would start looking familiar very quickly.
comment
created
vasily-kirichenko
username_1
About arrows
```fsharp
xCh *<-- x     vs    xCh *<- x
xCh *<-+ x     vs    xCh *<+ x
xIv *<-= x     vs    xIv *<= x
xMv *<<-= x    vs    xMv *<<= x
xMb *<<-+ x    vs    xMb *<<+ x
```
I prefer the right option. Although some of them don't look like very much as arrows, they are shorter. 

About `*<-=>` vs `*<--/=->`, I prefer the shorter version. 

About consistent use of `-` and `=`, I love the new operators. Much easier to remember.
comment
closed
polytypic
username_2
issue