I came here to say the same thing as the GP. So while I am not the poster you responded to, I'll pitch in my opinion.
I've written a number of Nim libs at this point. Rosencrantz is missing the idiomatic 'feel' of Nim -- blocks and trees. I would rather see a syntax like this:
let handler = firstOf:
get:
firstOf:
path("/api/status"):
ok(getStatus())
pathChunk("/api/message"):
accept("application/json"):
intSegment(id):
let message = getMessageById(id)
ok(message)
post:
path("/api/new-message"):
jsonBody(msg):
let
id = generateId()
saved = saveMessage(id, msg)
if saved: ok(id)
else: complete(Http500, "save failed")
I believe all of that should be possible using templates; grab the AST for the `stmt` passed to each function, then iterate over each node.
The problem with this approach is that it would be difficult to make handlers first-class. What if your post routes are a handler imported from another module?
Templates would allow for the syntax you present, but do not combine.
That's not at all the case. Templates can return values. So really, you could think of the templates as syntactic sugar around the existing functions you've got right now.
For example, the 'firstOf' template above would be responsible for scanning the AST it's given, taking each node and putting it in to a sequence. Then it instantiates a Handler with that sequence and returns it.
The problem is that they need to be given the AST. What if one constructs a handler somewhere in another module (say an authentication handler) and you import it? How can you reuse it, considering that it needs to surround other handlers (that is, if auth fails, you do not go inside inner handlers)?
With Rosencrantz it is easy: if `auth` is our authentication handler, and in your module you define a handler `h`, you can use `auth -> h`, or `auth[h]` if you want to spell `h` inline.
Now, what you could in fact do is write a template `authTemplate` that takes a handler `h` and returns `auth -> h`, so that you can still write in template style. But you have to do this for all handlers.
It could make sense to provide such templates for builtin handlers, though... want to chime in? :-)
In any case, I do not want to sound too critical. I would be happy to see a lighter syntax for Rosencrantz - I just do not know how to do it while preserving the composability of handlers.
If you have any proof of concept of how it might work, I would be glad to get a PR! :-) Ideally, it would be something that can be layered on top of the existing core, so that one could import "syntax helpers" from a separate module
Hi Andrea, as always, I'm very impressed by the diversity of high-quality, useful Nim projects you produce. :)
I agree with the other posters that the multi-line [ ] syntax is not very beautiful or Nim-idiomatic. As @Nycto suggests, nested code blocks would seem more Nim-idiomatic.
Also, I haven't seen anywhere else in Nim stdlib where multi-line [ ] expressions are used. In my mental model of Nim, [ ] is for 4 uses: 1. generics, 2. tuples/subranges, 3. array literals, and 4. array indexing. And none of these scenarios contain nested code blocks inside them.
For nesting of routes I would find nested code blocks most idiomatic. Or, if you find that nested code blocks simply do not work well, the key-value idiom of a table constructor {k:v} seems more idiomatic than [ ]: http://nim-lang.org/docs/manual.html#statements-and-expressi...
If it compiles (I admit I haven't tested it -- this is just off the top of my head), you could perhaps use a syntax something like:
get {
path("/api/status"): ok(getStatus()),
pathChunk("/api/message"): # etc.
}
Since the order of the (key,value)-pairs is preserved, this would automatically handle the ordered composition `h1 ~ h2`.
The problem with the syntax you propose is that it does not compose well.
For instance, in the example you give, `path`, `get` and `ok` are all handlers, but they appear in very different roles syntax-wise. It is not clear to me how one would add one more level, say to parse handlers.
I agree that the [] syntax is not the best-looking in the world, but I need something where I can do the equivalent of `h1[h2]`, where `h1` and `h2` are both handlers.
Templates and blocks would sort of work, but not in the case where both `h1` and `h2` are, say, stored in a variable. I am all for trying to obtain a better syntax, but I would not like to lose composability for that