Hacker News new | past | comments | ask | show | jobs | submit login
Rosencrantz – A Web DSL for Nim (github.com/andreaferretti)
69 points by Varriount on June 23, 2016 | hide | past | favorite | 28 comments



Nim is one of those underpublicized language. It's really good, I will leave it at that. I've used it a few times and it takes maybe 10 minutes to get back into it. It's perfect for making tools.


Yeah, I also had positive experience with nim. I'm not sure why it's so unknown, maybe because it fits in the same niche between rust and go and not backed up by any major company in industry. But I highly recommend trying Nim out!


Nim is great, and I use it (in production), but I know exactly why it isn't as popular: Nim is fucking huge.

Most programmers I know don't even know what a pragma is; Nim uses them extensively. Nim has types, objects, is low level, can be compiled to shared objects (something that didn't come out to Go until 1.5), macros, generics, templates, oh and it compiles to both C and JavaScript (and C++, and Objective C in case you need easy bindings for those), dynamic dispatch, mutually recursive types, pointers, named arguments, tricky hash tables, procedures that look like functions or methods (whichever suits you), and an irrelevant casing (spellLikeThis is the same is spell_like_this - case only matters for the first letter and underscores don't matter), and many more.

All of this on top of a hyper fast (~2x the speed of Go) compiled language.

Most other very complete languages are not compiled so you can kinda muck around in a repl and use introspection to figure out whats going on. Nim isn't like that. You need to think and figure things out when you're first learning it. Furthermore Nim isn't yet at 1.0 and lacks its killer library. Go and Rust are easier to get started with because they are simpler languages. They are easier to teach and easier to write documentation for.

Nim kinda reminds me of Ember or Rails or C++(see note): Huge, but worthwhile to learn because you'll never feel constrained by it. I highly doubt it will ever be as mainstream as Go though. I've generally found that most people are too lazy to learn harder languages / libraries.

Note: I do not like C++ for many reasons. The tooling is crufty, syntax is awful, impossible to reason about a codebase unless its picked a subset of C++; but the one thing I'll grant C++ is that you can always eventually get what you want done.


What you've just described is a hodgepodge of stuff thrown together. That's a lack of language design with a surplus of features. My readings on older reports for language designs showed the art was figuring out just how much power to include in the language & keeping it all consistent/sensible. One can overdo it.

An example of a clean, carefully-thought language that's low-level, consistent, and very powerful is PreScheme:

https://en.wikipedia.org/wiki/PreScheme

Modula-3, a C++ and Java competitor, was another where they made careful tradeoffs of features, safety, compile speed, run speed, and ease of learning/reading.

https://en.wikipedia.org/wiki/Modula-3

There's also some dialects of Haskell and ML focusing on system level. Didn't hit those levels of maturity though. What I'd like to see is someone take the Nim tech, subset it, change inconsistent stuff to be consistent... basically clean it up into a coherent picture... then deploy that as a new language. It could be pretty awesome if it happens. Right now, it's too complex and incoherent except for people, as you said, willing to do a huge learning curve.

Note: Nim is so huge now that it might be easier to add Ada static checks and Rust dynamic/concurrency checks to PreScheme instead of fixing Nim. You'd have a low-level, safe LISP. Put a 3GL front end on top of it for more adoption. That's what Julia people did.


Well I don't have a negative view of Nim in general, and I wouldn't describe it as a hodgepodge. I would describe Nim as a language that doesn't hold you back and doesn't save you from yourself. Personally I refrain from using most of the complex stuff, but when I need it I love how it is there for me. For example, the shared objects make it very easy for me to just roll things in Nim then link them to Ruby through the FFI.

Will your average developer need these features? No. But the average developer writes ASP, PHP, or JavaScript. I'd rather be formidable even if it means doing the work.


"and I wouldn't describe it as a hodgepodge. I would describe Nim as a language that doesn't hold you back and doesn't save you from yourself."

That's kind of vague. When I say hodgepodge, I mean they threw in all kinds of capabilities into the language without integrating them into a consistent whole. The examples I gave leave no surprises. What you see reading others' code is even expected. You could do about anything in a variant of PreScheme that you could do in Nim given almost any language feature gets ported to a LISP as a library. Except, it's all consistent in its style, syntax, and techniques for doing whatever it does. Nim seems to lack that very-significant property that affects uptake, ease of formal analysis, and maintenance burden.

So, the language is powerful. People are doing neat things with it. Just lacks design traits that have historically been important for programming languages in terms of getting and keeping developers/users.


> What I'd like to see is someone take the Nim tech, subset it, change inconsistent stuff to be consistent...

Here's someone who planned to create cleaned up subset of Nim: http://www.13rhinos.de/

I have no idea about viability of this project but it was his recent plan.


C++, Rust, D... other languages in this space are also quite huge in complexity.


> not backed up by any major company

That is pretty much the entire reason. Any technology not being backed by a large player in the industry has a much tougher time getting traction.


Are Rust and Go in the same niche?


No, not really. But nim in my opinion is something in between.


> It's perfect for making tools.

What does "tools" mean here and what is it about the language that makes it suitable for writing such things?

Edit: I fear I might have sounded snarky here, but it's just my "I want to learn" voice.


What did you use it for?


Slightly off-topic but related to Nim. Having been inspired by the Rust community survey we have just created a similar one for Nim. I would appreciate any and all answers, especially if you've never used Nim before!

http://goo.gl/forms/XJ3TPsaiIQe5HlTB2


Sad to see all these [] and () in Nim, which has such a beautiful syntax to start with.


What would you suggest instead? Routes are naturally nested, and overloading [] seemed the best choice


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.


Of course template can return values.

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`.


Hi jboy, thank you! :-)

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


Why not Jester[1]? Jester has a better API, IMO.

[1]: https://github.com/dom96/jester


I wrote Rosencrantz because I do not like the Jester API very much.

Jester, like many other frameworks, hardcodes exactly what to do with a Request - say parsing the headers, parsing the form if the content type suggests so, always return a date, and so on.

This is not bad per se, but I prefer to have something I can compose. Say, I have a working route `r`. If I want to add CORS support, for instance, I can just make a handler `h` that adds CORS headers and then use the route `h -> r`.

This is more flexible and leaves fine grained control over which headers to require, or return, how to do content negotiation, and so on. You can separate your routes for `POST` from those for `GET` in different modules and then combine them, or maybe separate your authentication routes from the business logic ones. You can easily create handlers that work as middleware, and so on.

In short, the thing I like about this approach is that handlers become first class objects that you can manipulate and create freely


I see. So, hopefully not being too shallow, this is like Jester with middleware support. I'll give it a try.


This is inspired by Spray, which I just learned about recently. The project is being migrated to Akka, so it's going to have support for the foreseeable future.

It looks like a great fit for Nim though.


Now I am waiting for a web server Guildenstern in Nim.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: