## History

This page documents a discord conversation held between Tomislav Grospic, Joshy Orndorff, and Isaac DeFrain 15 - 19 February 2019.

The question came up as to whether polyadic sends (analogous to multiple-argument function calls) added expressivity or convenience to rholang given that tuples are present as a ground term. After some discussion it was generally agreed that tuples alone are sufficient and maintaining a polyadic calculus adds confusion.

## Consequences

Isaac DeFrain's KFramework implementation of Coop Rholang does not currently support polyadic sends.

Rhoxy Does not support polyadic sends.

Simplified Rholang seeks to standardize a language without polyadic sends.

## Transcript

IsaacDeFrain02/15/2019 Tuple comms are working! I can't believe how easy that was, I was totally overthinking it before. My goal of showing something that is functioning next week seems very realistic all of a sudden :smiley_cat: tgrospic02/15/2019 This sounds great! I'll be happy to see it.

It seems that I missed you explanation, what exactly do you mean by "Tuple comms"? IsaacDeFrain02/15/2019 By tuple comm, I mean a comm of the form

```   x!(1,2,3) | for( a , c , b <- x ){ P(a,b,c) } -> P(1,3,2)
```

JoshyOrndorff02/16/2019 Congrats on getting it working! I'm cross-posting this longish example of why polyadic calculus isn't a good idea when you have tuples as a ground term. Curious what you guys think. Here's a nice way to write a forwarder that uses tupling inside a monadic rholang // Some destination contract that does something useful contract destination((x, y)) = {

``` // Do something useful with x and y
```

} |

// A contract that forwards messages to the destination contract forwarder (destinationArgs, ack) = {

``` // Acknowledge that the message has made it this far
ack!(Nil) |
```
``` // Pass the args on to the destination
destination!(destinationArgs)
```

} |

// Actually use the forwarder forwarder!((2, 3), *ack)

Here's how a polyadic rholang ruins that idiom // Destination contract is written in polyadic style contract destination(x, y) = {

``` // Do something useful with x and y
```

} |

// Forwarder now has to take a seperate argument for each destination argument // So this forwarder is only useful for arity 2 destinations now contract forwarder (destinationX, destinationY, ack) = {

``` // Acknowledge that the message has made it this far
ack!(Nil) |
```
``` // Pass the args on to the destination
destination!(*destinationX, *destinationY)
```

} |

// Actually use the forwarder forwarder!(2, 3, *ack) IsaacDeFrain02/16/2019 Thanks! That's right, you sent this to me earlier. IsaacDeFrain02/16/2019 In the first example, we listen on destination for a 2-tuple which we are viewing as a single process. In the second example, we are listening on destination for two processes which we view as a single 2-tuple. I'm not understanding why that's an issue. Doesn't it just give the programmer more options? JoshyOrndorff02/16/2019 In the second example, we are listening on destination for two processes which we view as a single 2-tuple. I'm not 100% sure what you mean by that, but I don't think it's right. The second example does not use tuples at all, it uses polyadic sends and receives. It would work just fine if rholang had no tuples at all.

The second code does work. Because it used polyadic sends, the payload that gets passed on from the forwarder to the final destination, is not a single item. Thus this forwarder only works when destination expects exactly two arguments communicated via a polyadic send. A completely new forwarder would be needed for a destination with 1 or 3 polyadic arguments. The first example OTOH can be used for any destination that accepts tupled arguments regardless of of the tuple's arity because it is always a single tuple. Maybe these examples help? Tuples and polyadic sends cannot be mixed. // Polyadic comm chan!(1, 2, 3) | for (a, b, c <- chan) { Nil }

// Monadic comm chan!((1, 2, 3)) | for (tuple <- chan) { Nil }

// Monadic comm with pattern matching chan!((1, 2, 3)) | for ((a, b, c) <- chan) { Nil }

// No comm (polyadic arity mismatch) chan!(1, 2, 3) | // Send is arity 3 for (tuple <- chan) { Nil } // Receive is arity 1

// No comm (polyadic arity mismatch despite pattern matching) chan!(1, 2, 3) | // Send is arity 3 for((a, b, c) <- chan) { Nil } // Receive is arity 1 (and needs a particular structure)

// No comm (polyadic arity mismatch) chan!((1, 2, 3)) | // Send is arity 1 for (a, b, c <- chan) { Nil } // Receive is arity 3 tgrospic02/16/2019 @JoshyOrndorff you want to say that forwarder!(2, 3, *ack) should be viewed as forwarder sending the 3-tuple? tgrospic02/16/2019 This is interesting for me from C# perspective because now it has tuples which have the same problem. print((2, 3)) means calling a function with a tuple. I'm more on your side to say that all functions in C# accepts only one argument, a tuple. :smiley: Maybe chan!((1, 2, 3)) should be the same as chan!(1, 2, 3)? tgrospic02/16/2019 I'm wondering if polyadic arguments are the problem if we have good implementation of the spread operator. In my compose exmaple I used list just because of that. https://gist.github.com/tgrospic/5e32fb9e9090c0a95af29c17e8e6c8a3#file-compose-rho-L9

// We can now deconstruct arguments contract forwarder (...@dest)

for(...@dest <- ...)

// But these things doesn't work :( contract forwarder (...@dest, ack)

@forwarder!(...dest) Or is there higher reason why spread operator cannot be implemented in more advanced way? IsaacDeFrain02/16/2019 I think I'm using the phrase "sending tuples" rather loosely here because in my implementation in k, I have the sorts

```      Send ::= Name "!" "(" Proc ")" | Name "!" RhoTuple
RhoTuple ::= "(" Proc ",)" | "(" Procs ")"
Procs ::= Proc "," Proc | Proc "," Procs
```

IsaacDeFrain02/16/2019 And also Proc ::= RhoTuple tgrospic02/16/2019 What are sorts in this contexts? Are the sort part of the parser for K syntax? JoshyOrndorff02/16/2019 I would prefer if the semantics in rholang were such that only single processes can be sent.

// Programmer writes chan!(1, 2, 3)

// Means same as chan!((1, 2, 3))