
Prologue: Full-Stack Web Framework Written in Nim - elcritch
https://github.com/planety/prologue
======
frankpf
I've been writing an interpreter with Nim and overall the language is really
good.

One feature that I'm excited for is DrNim[1]. It's not distributed with the
stable version just yet (you have to compile it yourself, but that's very
straightforward), but it's supposed to bring refinement types to Nim. These
allow you to write things like:

    
    
        proc safeDivide(a: int, b: int): float {.requires b > 0.} =
            a / b
    

and DrNim will check at compile-time that all code paths lead to `b` being
greater than 0, e.g.:

    
    
        var x = stdin.readLine.parseInt
        safeDivide(1, x) # DrNim will error at compile-time 
        var y = stdin.readLine.parseInt
        if y > 20:
           # no error since DrNim (using Z3) can
           # prove that y is always greater than 0 here
           safeDivide(1, y)
    

[1]: [https://nim-lang.org/docs/drnim.html](https://nim-
lang.org/docs/drnim.html)

~~~
steve_adams_86
I didn't know that this is called a refinement type! I've been wanting it
badly, both in Nim and in Rust. Now I have a way to search for what I'm
looking for to learn more.

Would it be possible to use this to ensure a string matches a certain pattern?
For example, maybe I want the string to look like a phone number or zip code.
I know I can use an abstraction to do this and have some level of safety, even
without a statically typed language, but I love the idea of implementing that
with primitives. As far as I know it isn't really a thing though. I'm assuming
a refinement is only really a refinement of a type within the same system, and
doesn't allow for special logic like pattern matching. That would be amazing
though.

I've spent so much of my career working with dynamic languages, it's hard to
know exactly which tools are available to me with these kinds of type systems.
I love it though. Learning to leverage types has been such a fun shift.

~~~
mixedCase
Not sure about Nim, but you can do this in TypeScript. There's even a library
called io-ts that allows you to create composable types of this form for
runtime validation which then turns the result into proper types.

~~~
steve_adams_86
Wow, that's really helpful. I'm working on a TS project at the moment and that
might come in handy. Thanks!

------
hombre_fatal
I played with Nim this weekend.

It's a shame that it did so many things right yet went with unqualified
imports by default.

After being spoiled by other languages, something even Node.js got very right,
I just don't want to try to get work done anymore in a language where you go
`import Lib` and then a bunch of unqualified identifiers like `foo()` are
available in the code.

I liked that Ruby feature as a beginner. But it didn't take long for me to
realize that knowing where things come from is worth the 500ms one-time-cost
you spend qualifying each import.

~~~
oehtXRwMkIs
That always bothered me too but never saw any good justification for that. I
feel like it's just a straight up flaw in the language, unless someone can
tell me why you would ever want unqualified imports.

~~~
Varriount
See
[https://news.ycombinator.com/item?id=23452857](https://news.ycombinator.com/item?id=23452857)

In short: unqualified imports don't play well with operator overloading,
templates, or generics.

~~~
hellofunk
I think you mean "qualified imports don't play well" from reading your other
comment.

~~~
Varriount
Woops, yes, you're correct

------
elcritch
Note that this won't currently run on embedded devices. The ARC GC release
with Nim 1.2.0 could be really useful on embedded processors the Async story
hasn't been ported and contains cycles which leak memory. It works great ...
until you OutOfMemory and it resets. The Nim core team is working on it. :-)

Still IMHO, Nim has a lot of potential on embedded devices. I've been using it
to augment some C code I'm using on an ESP32 and it integrates pretty
smoothly. It's enjoyable to write Nim code and I can do hacky C-like tricks
with pointers if I need to which is great. Writing C isn't to bad until you
want to just have some generics and containers like lists and hashes. C++ has
them of course, but personally just to much headache for me.

------
hliyan
Only obliquely related: I was hoping Nim would be the kind of language that
preferred libraries over frameworks.

We have way too many frameworks and not enough libraries. A library comes with
an API. A framework must arrive with its own architecture, a vocabulary to
describe that architecture, tooling, a plugin ecosystem, configurations and a
lot of best practices and recommendations. Are we paying too much for that
initial, one-time effort of bootstrapping an app and stitching a bunch of
libraries together into an app?

~~~
jgwil2
How many people are developing commercial software without a framework?
Software needs architecture, tooling, best practices and the rest. Taking the
library approach just results in ad hoc frameworks emerging over time.

~~~
ddevault
Virtually all C programmers, for one. Most Go programmers, too.

~~~
ies7
If I have a bunch of libraries that I always use (or at least 80% of them) to
start a new project, isn't that a framework?

~~~
wffurr
A framework is not a collection of libraries. You are still writing the top
level code that glues all the libraries together.

A framework is that top level code, and it provides extension points or a
pluggable system for the parts the framework author anticipated you needing to
customize.

Works great within the boundaries, but gets very difficult once you want to
step outside the framework author's preconceived notions about what one would
want to do with their framework.

With the library approach, you have to invent some of your own conventions for
routing and such at the top level, but you have a full understanding of it and
can more easily extend it to handle new use cases.

Most projects outgrow a framework, at which point they tend to invent their
own but it's a huge amount of technical debt to handle.

------
danpalmer
This looks nice, but I'd question what about this is "full stack"?

While full stack frameworks are unlikely to actually build at every layer of
the stack, I'd expect them to be aware of and do something to address each
layer. At a minimum I'd expect an ORM of some sort, something around
validation of data from the client, bits for building APIs, potentially a
structure for building frontend assets, tooling around deployments and maybe
migrations.

This isn't what a lot of developers want, but it is full-stack. Maybe more
things should be happy _not_ being full-stack and owning the idea that they
are good at one part of the stack.

------
alberth
I’m surprised no one has mentioned Jester. It’s the web framework powering the
official forum site for NIM.

[https://github.com/dom96/jester](https://github.com/dom96/jester)

[https://forum.nim-lang.org/](https://forum.nim-lang.org/)

------
giancarlostoro
As a full time Python developer (though it's not my only tool in the toolshet)
I've been looking at Nim for a while. I think it looks healthy currently. I'm
wanting to give it a shot, and projects like Prologue are what I'm sort of
waiting on before I dive in there.

There's a few things that concern me, like looking at the tutorial the echo
statement is not a function, reminds me of Python before Python 3. Also the
pascalCase function for readLine. I think I see why Python mainly uses single
word functions for things, to avoid the debate about variable naming
conventions as much as possible. Though now the community agrees on
underscores mainly. Though this doesn't concern me as much so long as it
remains consistent.

~~~
matthiaswh
The `echo` statement is indeed, a function. The tutorial is taking advantage
of Nim's optional parenthesis when calling the function.

    
    
      echo "Can you hear me?"
    

is the same as

    
    
      echo("Can you hear me?")
    

Nim also has somewhat unique rules about casing and underscores for variables
and names. `readLine` is the same as `read_line` is the same as `rEadLInE`.
However, the first letter must be the same (there are reasons for this), so
`Readline` is not the same as all of those. The idea is that you can choose
your preference and not have to think about what anyone else chooses. If a
library uses pascalCase and your prefer snake_case then you can use
snake_case.

This is a big point of contention for many people, but in practice, in my
experience, it is never an issue.

------
zesily
looks good

