Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Thanks for being honest with everything, I found this to be a very balanced answer.

What would you say are the advantages of Nim over a language like Go? I see a lot of similar "value add" statements.



Happy to hear that you found my answer balanced :)

> What would you say are the advantages of Nim over a language like Go? I see a lot of similar "value add" statements.

In all fairness I haven't given Go a proper try. But my main problems with it, based on what I've seen of it, are the following:

* Lack of generics

* Lack of exceptions

* I prefer the Python-like syntax of Nim

* Metaprogramming in Nim is fun and powerful

* Lack of (official) package manager


> Lack of exceptions

Go does support multiple return values, commonly (T, error). If you think about it for a while, you will realize a exception is just a type of return value.

Regarding generics, the more I used golang, the less I had a need for generics. Also, there is solutions if you insist, such as https://github.com/cheekybits/genny


Sure, they are, but they're sums, not products. Go's failure to acknowledge this leads to infecting every type with the Billion-Dollar Mistake in an effort to encode sums as products (and loses out on compiler checking of it, in the process). The syntactic aspect of exceptions is nice, but can be handled without compiler magic if you have nice syntax for monads in general.


> Go does support multiple return values, commonly (T, error).

Sure, and Nim supports multiple return values too. But having to handle this extra value at every function call seems like it would get old really fast. I don't want to worry about every single exception when I am prototyping for example. Also, without stack traces, how do I know where an error comes from?

> Regarding generics, the more I used golang, the less I had a need for generics. Also, there is solutions if you insist, such as https://github.com/cheekybits/genny

Indeed, like I said, I haven't used it much so perhaps I would be able to live without generics. It is still very nice that Nim offers them though.


> I don't want to worry about every single exception when I am prototyping for example.

You are free to ignore the returned error, so for example to compare with a situation where you would raise an exception in another language inside a function, but not wrap the call in a try-catch:

val, _ := funcThatCanFail()

> Also, without stack traces, how do I know where an error comes from?

Go does have stack traces


> exception is just a type of return value

No, they are part of the syntax. The compiler (in Nim and other languages) can track which exceptions are allowed to be raised in a procedure and which aren't at compile time:

https://nim-lang.org/docs/tut2.html#exceptions-annotating-pr...


And golang knows what type of error can be returned at compile time.

    type FatalError error
    func foo() FatalError {}


Nim does too. The exception tracking is performed at compile time.


    * Lack of generics
    * Lack of exceptions
I don't see these items as being shortcomings. Go is simple and simplicity is elegance, and simplicity is something that cannot be found in most modern languages. Just look what happened to C++, It has become more like C# and Java. Regarding Nim, I believe it's a nice language and I have done a few small projects using it but just like what I mentioned there are 10000 ways to do a single task and that sometimes is confusing.

BTW C/Go/lua are my languages of choice


Being fluent in Python and Go, I definitely prefer the explicit error handling in Go as opposed to the try-catch error handling in python where most of the time you can never really be sure of all the possible errors which can be thrown. I spent some time learning Haskell, and one of the points that stuck was that in languages with exception handling, a function will have some kind of declared result, but all the different exceptions which can be thrown are also basically alternative return values. Any caller of the function then has to know all possible return types to effectively handle all errors, but since most code doesn't document which errors might be thrown, it's a crap shoot. Functional languages basically force the return to always be the declared type, and go mostly follows this model.


Even though I don't like Go's explicit error handling, I think this is a very reasonable comment.

I don't like Go's explicit error handling because (1) I think it clutters up your main code path with error-handling logic, and (2) it forces you to always handle errors locally (even if that local code does not have the context to know how to handle the error) or return the error code through multiple layers of functions (back to where it can be handled).

That said, I completely agree that exceptions are also problematic. As you say, you can never really be sure of what exceptions can be thrown. Some languages have a "throw" keyword, in which you're meant to enumerate the list of possible exceptions; but of course, that's a headache to maintain, and is affected by inner code (such as library code) that might be completely out of your control or review. And when should the "throw" keyword be enforced, at compile time or runtime? And what should happen if the "throw" keyword's list of possible exceptions is violated?

Then of course there is the other problem with exceptions, the flip-side to being forced to handle an error locally: As your exception unwinds the stack, it might obliterate some local context that is needed to decide how to handle the error; or it might obliterate some local context that is needed to continue with your original task after the error has been handled!

The most interesting (and least broken) error-handling mechanism I've encountered is Common Lisp's "condition" system: http://www.gigamonkeys.com/book/beyond-exception-handling-co...

I see C++'s `std::set_new_handler` function [0] as C++'s less-capable equivalent to Lisp condition handlers. But the function invoked by `std::set_new_handler` lacks the ability to assess local context to decide how to handle the problem.

[0] http://www.cplusplus.com/reference/new/set_new_handler/

EDIT: I would love to see a language like Nim incorporate something like Lisp's condition system. Nim already offers "procedural types" (function pointers) [1] and closures [2] to capture variables from the enclosing scope. Nim even offers "anonymous procs" [3], to avoid the need to define new error-handling functions everywhere.

[1] https://nim-lang.org/docs/manual.html#types-procedural-type

[2] https://nim-lang.org/docs/manual.html#procedures-closures

[3] https://nim-lang.org/docs/manual.html#procedures-anonymous-p...


> That said, I completely agree that exceptions are also problematic. As you say, you can never really be sure of what exceptions can be thrown. Some languages have a "throw" keyword, in which you're meant to enumerate the list of possible exceptions; but of course, that's a headache to maintain, and is affected by inner code (such as library code) that might be completely out of your control or review. And when should the "throw" keyword be enforced, at compile time or runtime? And what should happen if the "throw" keyword's list of possible exceptions is violated?

This is something that Nim offers via the `raises` pragma[1]. It is enforced at compile-time and in my experience works rather well.

1 - https://nim-lang.org/docs/manual.html#effect-system-exceptio...


@dom96: what do you think about a Common Lisp-like "condition" system [0] in Nim?

[0] http://www.gigamonkeys.com/book/beyond-exception-handling-co...


> Go is simple and simplicity is elegance

Can we please let go of this meme? Many polyglot developers complain about the lack of generics, and how Go code ends up being more verbose and less expressive than languages like Python.

Things like interfaces and pointers to interfaces, structs vs slies and maps are not consistent.


>Things like interfaces and pointers to interfaces, structs vs slies and maps are not consistent.

Yeah, and inconsistencies are one of the things that make for a more complex language, not a simpler one. In fact, Go has generics, but they only work for magic, built-in types. I would argue that's less simple than just supporting generics, and building your standard library out of language features.




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

Search: