I've been programming rholang for about 5 months now and in general I love it and am super happy I learned. If you want to join in that journey, checkout out my tutorial. In those five months, though, I've also noticed a few pain points where I think syntactic changes would go a long ways. I've opened issues or started discussions about some of these already. But I'll write them down here too.
Contract Definitions
The syntax for a contract definition requires an equal sign which isn't required in similar constructs like for
.
// The current way contract x(y) = { Nil } // The wishlist way contract x(y) { Nil }
Trailing Pars
Styleguides (like this one) often recommend including a trailing comma in multi-line list, tuple, object , etc literals. I would like to do the same with my pars in rholang. It makes them feel more like the familiar semicolons at the end of each line. The semantics are that the unary par is sugar for a par with the stopped process.
// The current way new x in { @0!(Nil) | @1!(Nil) } // The wishlist way new x in { @0!(Nil) | @1!(Nil) | }
Convenient Unforgeable Processes
It's great to pattern match data with a preceding @
sign when you want to use it as a process. I want to do the same thing with my new
s.
// The current way new x in { *x } // The wishlist way new @x in { x }
We don't need all of pattern matching there. Just this one special case for then I want to make a new ack channel and immediately send it.
Less Nesting From New Names
Rholang code gets really deeply nested. Some of that nesting feels natural like in contract
s or for
s. But it's really common to then create new names immediately inside causing more deeply nested code. I wish we could combine those things.
// The current way for (a <- b) { new x, y, z in { Nil } } // The wishlist way new x, y, z in for (a <- b) { Nil } // Alternate wishlist way for (a <- b) { new x, y, z here | Nil }
The Peek Operator
I believe there are already plans tom implement this <!
peek operator. It receives a message from a channel, and concurrently writes the same value back to the channel.
// The current way contract getVal(return) = { for (@val <- valCh) { return!(val) | valCh!(val) } } // The wishlist way contract getVal(return) = { for (@val <! valCh) { return!(val) } }
A crazier idea is to do the same kind of thing for receives instead of just sends. As soon as I receive, immediately put the continuation back listening. I guess this might be hard or impossible. Is it even possible to do that manually in rholang?
Sane Sequential Programming
Rholang is inherently concurrent. It's a wonderful new way of thinking and I'm glad it's so easy to do now. There are also times when things need to happen sequentially. Like printing the heading of a table before the data rows, or integration testing. Sequential code gets really deeply nested, and thus really hard to read. What if we bring back the classic ;
from sequential languages such as c or java for this task.
// The current way new ack in { stdoutAck!("This is a haiku", *ack) | for (_ <- ack) { stdoutAck!("So the line order matters", *ack) | for (_ <- ack) { stdoutAck!("Because syllables!") | } } } // The wishlist way // The contracts must be of the right form so an ack can be appended to the arg list new ack(`;`) in { stdoutAck!("This is a haiku"); stdoutAck!("So the line order matters"); stdoutAck!("Because syllables!"); }
This combines extremely nicely with my trailing | idea, so I've writing it that way. Remember there is an implicit Nil
after the trailing par. That Nil will become the body of the last for
comprehension during desugaring. Thus the programmer can call the same contract that expects an ack every time and not have to write a special one (equivalent of stdout
without the Ack) to handle the last call.
Timer in Powerbox
This would be useful for writing games and stuff, but probably not for the blockchain. And it may encourage bad practices for handling race conditions. But still, I need to to implement a realistic AI in my game.
This one works brilliantly with my sequential idea from above, so I'll write it that way. But this could still be implemented without that previous enhancement. It would just lead to deep nesting.
// There is no current way // The wishlist way new timer(`rho:os:timer`) in { stdoutAck!("three"); timer!(1000); // milliseconds stdoutAck!("two"); timer!(1000); stdoutAck!("one"); timer!(1000); stdoutAck!("blastoff"); }
Console Access in REPL
The REPL (read, evaluate, print, loop) is a convenient way to test little snippets of rholang code interactively. Such a thing is precedented in other languages like python's interactive mode, haskell's ghci, and every browser's javascript console. It is extremely common to print an output to the screen for quick evaluation. But in rholang it takes a lot of code to get access to such capabilities which is a pain to type each time.
// The current way new stdout(`rho:io:stdout`) in { stdout!("Hello World") } // The wishlist way (REPL ONLY!) stdout!("Hello World") // Just works ;)
I'd love to hear any thoughts or ideas!
-Joshy