Please, leave exceptions there, there are other methods to handle errors, but they are often cumbersome in an imperative language. Bitsets are also really nice, and they play well with the c pattern of using integers for options and combining them with masks, just in a more readabe way.

That said, these are things I do not use:

  • methods
  • converters
  • discardable
  • TaintedString

A large standard library is super convenient: I am happy not to have to reach nimble for a lot of common task, such as writing a webserver or parsing json. Incidentally, it also allows nimble library to have very few dependencies in general, which is good. That said, there are things that I think may be safely removed:

  • matchers
  • basic2d
  • basic3d
  • mersenne (why doesn't it integrate with random?)
  • parseopt (but not parseopt2)
2018-04-23 08:14:28

I would say I need converters from the list the most without them vectorization would have been a problem. Mixing scalar and vector types happens in every line of code I have, without scalar to vector converter it would have been such e pain.

I don't see how borrow can be a macro if you borrow something like + and you would like to avoid extra functions to be emitted, somehow compiler's codegen needs to be aware.

I like Nim's enums and I often have to import C++ enums so I can't choose if they are with holes or not, yet need to wrap them. Distinct ints are not anywhere near in terms of usability: I need to know how many distinct values out there, have string <-> value parse/format.

I vote:

  • discardable
  • Use exceptions less, have exception free alternatives in stdlib.
2018-04-23 08:16:51

Not sure if this was ever discussed so I will throw this idea here:

The Nim compiler has very complicated built in type rules for int types handling, to make int, int16, uint types mixable without explicit type conversions. Is is possible move out this logic from compiler into a set of converters defined in stdlib module? Users preferring strictness would not import it and they will need to convert types explicitly, while those who are relaxed can import it.

Disclaimer: It is untested idea

2018-04-23 10:04:53

I like Nim's enums and I often have to import C++ enums so I can't choose if they are with holes or not, yet need to wrap them. Distinct ints are not anywhere near in terms of usability: I need to know how many distinct values out there, have string <-> value parse/format.

This can be done with a macro.

2018-04-23 10:11:33

One more idea.

Can it be more federated decision? For example: if we have a volunteering maintainer to support a feature and to resolve its reported issues then it will remain. So people instead of theoretical discussion will vote with their time (real asset) instead? I think you will likely to come up with realistic/pragmatic design in this case and over time build a team of maintainers.

2018-04-23 10:48:00
That's a very good idea but it's not universally applicable, a feature needs to justify its implementation complexity and the training/documentation aspects (a bigger language is harder to teach etc). Also, when I say something like "I want to remove X from Nim" I generally mean to provide a better mechanism that also happens to do / allow for X. For example, converter could be turned into a more general "overloading resolution hook" that is consulted when type checking a call expression failed. Note that this is just an example, I doubt an "overloading resolution hook" is what Nim needs...
2018-04-23 11:16:42

On the topic of method: I like the flexibility that it provides, but I recognize that many if not most of the community doesn't like it's current state. I am not a die-hard OOP programmer - e.g. I prefer a Rust or C style where one uses either custom structures or uses composition.

However, without method OOP support in Nim would be quite fundamentally limited. I have not myself used any packages that would really need OOP, but they exist. For example, GUIs are IMO one of the best applications of the paradigm. It would be a shame to just "toss out" the functionality, and require manual or macro based solutions akin to those in C.

Despite this, there are more than a few challenges with method. For example, Nim supports excessive overloading - but this does not play well with excessive overriding. It turns out that methods shadow each other in a first-declared (or first defined?) fashion, which results in problems like import order dependent ``method`` behavior.

This is not to say that method is all bad: it has the benefit of inlining which closure based implementations lack. This is due to the dynamic dispatch tree implementation - which I quite like. I'd hate to see that eliminated entirely.

If method were to go, I imagine that few programmers really want to go the GObject route and build a VTable from scratch for each type. This means that most will turn to Nim's incredibly powerful metaprogramming capabilities to implement overridden methods/abstract classes/interfaces.

So - if we do go the macro route with replacing method, we need to take a proactive approach and include a decent macro somewhere (in the stdlib?). What I wouldn't want to see is each and every package implementing the functionality differently and incompatibility. After all, the role of the stdlib is to provide a foundation for all packages and binaries combined. If we don't provide a solution, people will roll their own, well done or... not well done. Nim advocates for expressive code, but I almost think that in this particular case, promoting a orthogonal solution is better than promoting a custom and poorly integrated solution.


TLDR: If method goes we still need a solid alternative that still has decent expressiveness and performance. I don't think that dynamic dispatch is something that "should be removed".

2018-04-23 16:51:18
Well there is an RFC to make some of the "interfaces" nimble packages part of the stdlib. Again, let me clear: When I say "remove X from Nim", I mean "replace X by something better/slightly different (that in the best case can be done with macros)".
2018-04-23 17:51:10

How do you feel about exceptions? I always disliked exceptions in C++ and found comfort in C, which lacks them.

Yet, when I found Nim I realized that exceptions aren't so tricky. Perhaps this is because of 2 really important features:

  • The GC: In C++ you must take extra care to write exception safe code. RAII and exceptions must go hand-in-hand, and it's a tiring mental overhead. In Nim, Stuff Just Gets Deallocated (tm). I always associated exceptions with GC languages, so they fit better into Nim than C++. Of course, we are now developing a new run-time that will do away with many GC complexities. This shouldn't affect the exception system though, am I correct? The refcounting will just be deterministic instead of deferred and cycle-resolving??
  • The defer keyword: Really this makes resource management doable. Until destructors are 100% ready it's really The Way to handle non-ref types, since these types can't be "finalized".

So I'm not against exceptions in principle for Nim specifically. However it would be a good idea to note that many targets of a "systems language" don't favor exceptions, because they both change control flow and can be expensive (since Nim targets C++ we can use zero cost exceptions and performance isn't an issue). The only issue then is philosophical: how does one handle errors? Is it at the call site w/ a return value (no exceptions - my favorite in general), through unwinding exceptions (current state of things), through multiple return values (like Go), through unrecoverable panics (terrible) or through some state like errno (really terrible).

IMO, the only alternative is something like Result<T, E> in Rust or Go-like tuple return values. We would need a chaining solution like the ? in Rust. I don't want this:

try:
  obj.getInner().upgradeToRwChannel().attachListener(callback)
except Something:
  echo getCurrentExceptionMsg()
  handle()

... to turn into this:

let inner = obj.getInner()
if inner.isErr:
  handle()

let upgrade = inner.upgradeToRwChannel()
if upgrade.isErr:
  handle()

let res = upgrade.attachListener(callback)
if res.isErr:
  echo res.msg
  handle()

2018-04-23 18:11:42

I'm not a fan of exceptions but the alternatives that are usually suggested are worse reinventions of exceptions.

Error handling is really not that hard to analyse: In case of an error you can only do a couple of different things:

  • Let it "bubble up" the call stack.
  • Retry the operation.
  • Report/discard and continue with the next instruction whatever that means.
  • Trap/quit/die. Arguably that is just a shortcut for bubbling up the call stack and let the OS's "exception handler" deal with it.
  • Turn it into a regular value, no Option[T] or Either[T, E] do not count. <-- I'm personally in favour of this, but nobody agrees with me and in theory it can make errors harder to diagnose.

What exceptions do is they claim "let it bubble up is what you want in over 90% of all the cases out there so we make this the implicit, fast, default model of operation". To refute exceptions you need to come up with a decent statistic that proves this wrong or you need to argue that "implicit is bad". "Implicit is bad" ignores too much the point of using computers in the first place in my opinion which leaves us with (b) statistics. So bring them on, I say.

2018-04-23 19:02:44