D vs nim

PR’s welcome! Goal: up to date and objective comparison of features between D and nim, and 1:1 map of features to help D users learn nim and vice versa.

NOTE: tables render better (ie full width) with a chrome extension, see https://stackoverflow.com/a/49566642/1426932

NOTE: occasionally, nim code below will use the braces syntax skin to fit in 1 line

Help welcome, eg by filling in the entries with ? TODO and CHECKME, correcting wrong entries, or adding more (see also other files eg libraries.md).

category D nim 1 for D, -1 for nim
UFCS      
UFCS supported everywhere not everywhere, eg: mixin(expr), typeof(expr), local symbols (called UFCS or MCS in nim) not for cast, not for templates/macros with untyped, see https://nim-lang.github.io/Nim/manual.html#templates-limitations-of-the-method-call-syntax  
tuple      
tuple unpacking no yes:proc getTup(): auto = (4, "foo"); let (_, a) = getTup() -1
CTFE      
engine AST interpreter. every expression encountered will allocate one or more AST nodes. Within a tight loop, the interpreter can easily generate over 100_000_000 nodes and eat a few gigabytes of RAM. That can exhaust memory quite quickly. Future work: https://dlang.org/blog/2017/04/10/the-new-ctfe-engine/ uses a register VM (also the basis of Nimscript); faster -1
FFI during CTFE no no 0
embed directly C/C++ code no yes: emit -1
can read/write/exec during CTFE read only (string import) yes; allows filesystem access via staticRead and staticExec; -1
other CTFE limitations ? Heap allocated compile-time variables are turned into immutable static/const at runtime. ?
OOP      
design Java like allows multi-method dynamic dispatch (defined outside, avoiding kitchen sink classes) -1
syntax      
allows local imports yes no 1
import order irrelevant yes ? 1
package modules (allows backward compatible breaking of module into package) yes: https://dlang.org/spec/module.html#package-module yes but see https://github.com/timotheecour/D_vs_nim/issues/22 0
mutually recursive imports yes no, compile-time error. you can use forward declaration and/or “mixin foosymbol” to tell the compiler that a symbol will be visible at one point (CHECKME); see also: https://stackoverflow.com/questions/30235080/cannonical-way-to-do-circular-dependency-in-nim, https://github.com/nim-lang/Nim/issues/3961, https://forum.nim-lang.org/t/2114; workaround: The lack of cyclic dependencies in Nim is usually worked around by having a types module 1
familiarity C-like C or Python-like 0
interpolated strings no yes -1
named parameter arguments no yes -1
style (subjective opinion) https://dlang.org/dstyle.html ; style guide for phobos takes too much vertical whitespace (eg braces on their own line) Nim Enhancement Proposal #1 0
types      
mutually recursive types yes these types can only be declared within a single type section (else would require arbitrary symbol lookahead which slows down compilation.) 1
nested types yes no; but see https://github.com/nim-lang/Nim/issues/7449 1
semantics      
rvalue references no ? ?
attribute inference for template functions ? ?
Distinction between traced and untraced pointers   yes -1
forward declarations allowed? yes no; see https://github.com/nim-lang/Nim/issues/5287 1
User defined operators partial: opCall opSlice, opAssign etc yes -1
User defined attributes yes ? ?
RAII yes (modulo caveats https://github.com/timotheecour/D_vs_nim/issues/27) no, see: RAII 1
prevention of null dereferencing <p>manual null checks required</p> <p>null pointers to class objects can exist. The only way to check for them is to remember to use if (myObj is null). Lessening the impact of this, many built-in types, such as integers, fixed-size arrays, maps, and struct objects, can never throw null dereference errors, either by not being null or by treating null values as empty values (source).</p> <p>manual null checks required</p> <p>nil references and pointers can exist. The standard way to check for them is to remember to use if myObj == nil or if myObj.isNil. Lessening the impact of this, many built-in types can never be nil, including seqs. Only ptr and ref types (and cstrings) can be nil.</p> <p>Nim also has an experimental notnil feature that enables a not nil type annotation, e.g. ref SomeObject not nil, to ensure that a variable can never hold nil. This feature was made experimental in 2018.</p> ?
debugging      
maturity      
stability few breaking changes in each release few breaking changes in each release 0
community larger   1
interop      
C++ Calpypso (ldc fork) allows direct C++ integration   1
C++   C/C++ code generation giving us much better interop than what D offers. Case in point: Converting to cstring doesn’t require an allocation and copy; see also https://github.com/timotheecour/D_vs_nim/issues/12 -1
can compile to js   yes -1
direct use no Nim emits C code and you can break in with emit pragma; C code doesn’t have to be written outside nim file -1
standard library      
ranges D ranges (implements empty, front, popFront) yield-based iterators ; maybe simpler to write but less efficient? not as flexible? (eg: can’t do infinite ranges, bidirectional ranges) ?
variable length arrays D allows alloca see https://forum.nim-lang.org/t/499 (Variable length array) ?
slices form pointer + length builtin, T[] pending https://github.com/nim-lang/Nim/issues/5753 or https://github.com/nim-lang/Nim/issues/8256 1
strings are built from arrays yes, immutable(char)[] no, different type, so less generic API’s 1
lazy functional programming (eg map, filter) yes, eg std.algorithm no (pending https://github.com/nim-lang/Nim/issues/3837, https://github.com/nim-lang/Nim/issues/8188) 1
ecosystem      
contributing PR’s languish forever PR’s get merged way faster in nim (see https://github.com/nim-lang/Nim/pulls vs https://github.com/dlang/dmd/pulls or phobos etc but not sure how to quantify objectively; see also https://github.com/nim-lang/Nim/pulse vs https://github.com/dlang/dmd/pulse). QUOTE: Nim is magnitudes of orders easier to contribute to. Not only the compiler code is easier to reason about (at least for me), but PRs are accepted a lot more willingly. I bet such openness of the core devs makes Nim evolution faster and I hope it’s gonna stay that way no matter 1.0. -1
repo split dmd,druntime,phobos single repo for compiler + stdlib making synchronization easier -1
github history highly intertwined (uses merges) almost linear (guessing it rebases) -1
issue tracker bugzilla (issues.dlang.org) github issues (https://github.com/nim-lang/Nim/issues) -1
opened/closed bugs 4588/14061 (https://dlang.org/bugstats.html) 1296/3437 https://github.com/nim-lang/Nim/issues ?
packages      
packages dub: https://code.dlang.org/ nimble: https://nimble.directory/packages.xml and https://github.com/nim-lang/packages 0
number of packages (as of 2018/07/13) 1346 (http://code.dlang.org/) 704 (nimble list ) 1
tooling      
format code dfmt --inplace nimpretty, not yet ready: https://github.com/nim-lang/Nim/issues/7420 1
code reduction for bugs dustmite pending https://github.com/nim-lang/Nim/issues/8276 1
REPL https://github.com/dlang-community/drepl ; https://github.com/callumenator/dabble unofficially, you can use nim secret but it does not support a lot of things. The most promising is nrpl ?
implementation      
GC single shared memory heap that is controlled by its GC, thread safe, fully conservative, stop-the-world precise, thread-local heaps, a bit more deterministic and a lot faster, you can even timeframe it if you need consistent 60fps for example. Much better GC implementation for soft real-time applications because it can be paused or the max pause can be tuned. Default GC is not thread safe. GC implementation can be switched at compile-time between deferred reference counting with cycle detection (default), mark and sweep, boehm or no GC (memory regions). Untraced heap-allocated manually managed objects are available (nim distinguishes bw ref and ptr: traced references point to objects of a garbage collected heap, untraced references point to manually allocated objects or to objects somewhere else in memory) -1
compile speed faster (via dmd) CHECKME   1
is compiler bootstrapped? frontend, not yet backend yes, and bootstrapping sources can be regenerated from nim allowing compiler sources to use latest features -1
error messages uses poisoning/gagging to avoid spurious errors yes ? ?
binary sizes produced   produces smaller binaries -1
shared library support linux:OK; OSX: ldc (not dmd); windows: not OK(CHECKME) ; anything that can be linked from C -1
doc      
builtin doc ddoc (noisy and nonstandard) reStructuredText eg ` ## removes n from L. Efficiency: O(1).` (eg: https://nim-lang.org/docs/lists.html) -1
metaprogramming      
variadic generics yes: void fun(T…)(T a) no; RFC: Variadic Generics; but varargs[untyped] allowed in macros; not same though, eg can’t be used in template functions 1
partial type template type deduction yes no, see https://github.com/nim-lang/Nim/issues/7529 1
supported generic parameters type, alias, constant type, alias, constant 0
template constraint void fun(T)(T a) if(isFoo!T) concepts are simpler to use: type isFoo = concept a (...); proc fun(a: isFoo) -1
macro no hygienic macro system instead of string mixin; string mixin are available through parseStmt. The macros modify directly the abstract syntax tree given by the parser, before the compiler pass. It is possible to implement new DSLs based on the macro system: for example webserver DSL jester -1
backend      
available backends custom (dmd), gcc (gdc), llvm (ldc) C, C++, js; WIP llvm (https://github.com/arnetheduck/nlvm) ?

See also libraries.md

features requested in latest D survey (https://rawgit.com/wilzbach/state-of-d/master/report.html) that are already supported in Nim

compilation time

runtime performance

category/benchmark D nim 1 for D, -1 for nim
functional: Zero_functional; Zero_functional fuses loop at compile-time when chaining zip.map.filter.reduce functional constructs ? (missing D entry) nim is currently number 1 or 2 against 9 other langs. The other number 2 or 1 lang being Rust. ?
webserver ? Mofuw by @2vg is faster than tokio-minihttp, the current #1 on TechEmpower benchmark ?
parsing csv files csv-blog-d csv-blog-nim; D and Nim had the same speed and same compilation time. fastest CSV parser (to parse GBs of machine learning datasets) is XSV in Rust 0

See also https://github.com/timotheecour/D_vs_nim/issues/11

similar code comparison

category D nim
longest path https://github.com/logicchains/LPATHBench/blob/master/d.d https://github.com/logicchains/LPATHBench/blob/master/nim.nim
csv test https://github.com/euantorano/faster-command-line-tools-in-nim/blob/master/D/csv_test.d https://github.com/euantorano/faster-command-line-tools-in-nim/blob/master/Nim/csv_test.nim
rosettacode examples https://rosettacode.org/wiki/Category:D https://rosettacode.org/wiki/Category:Nim

differences (not clear if pro or con)

map of corresponding features

| category | D | nim | | — | — | — | | syntax:lexical | | nesting block comments | /+ +/ | #[ ]# | | documentation comments | /** */ or /++ +/ or /// | ## or ##[ ... ]## | | WYSIWYG string | `foo\nbar`, r"foo\nbar", etc. | """foo\nbar""", r"foo\nbar", etc.(?) | | end of file (useful when debugging) | __EOF__ | ? | | increment | i++ | i+=1 or inc(i) | | concatenation | ~ | & | | empty statement | {} | discard | | null pointer | null | nil | | checking for null | if (myObj is null) | if myObj == nil or if myObj.isNil | | syntax:parsing | | alias | alias T2=T; | ?; template, see https://github.com/nim-lang/Nim/issues/7090 | | type alias | alias T2=T; | type T2=T | | string mixin | mixin("1+1") | stringMixinLikeInD("1+1") with: macro stringMixinLikeInD(s: static[string]): untyped = parseStmt(s) source: https://forum.nim-lang.org/t/1779/2#19060 | | UFCS | foo(a, b), a.foo(b) | foo(a, b), a.foo(b), a.foo b, foo a, b | | expr without parenthesis | auto a=fun; calls fun | var a=fun returns fun | | UFCS expr without parenthesis | auto a=b.fun; calls fun | var a=b.fun calls fun (when b is arg, not module) | | static if .. else if .. else| static if(foo1) bar1 else if(foo2) bar2 else bar3 | when foo1: bar1 elif foo2:bar2 else:bar3 | | conditional compilation | version(OSX) | when defined(macosx) | | compile time if | static if | when | | string import | import("foo"); requires -J for security | staticRead("foo") | | public import | public import foo; | import foo; export foo; | | static import | static import foo; | from foo import nil | | package fully qualified access | pkg.mod.symbol(); | symbol() (pkg.mod.symbol illegal) | | syntax:exceptions | | scope guards | scope(exit) foo, scope(success) foo, scope(failure) foo, | defer: foo, ? , ? ; see also https://forum.nim-lang.org/t/141/1#23104 | | try throw catch finally | try{throw new Exception("bar");}
catch(Exception e) {writeln(e);}
finally {} | try: raise newException(IOError, "test exception")
except IOError: (let e = (ref IOError)(getCurrentException()); echo e[])
finally: discard | | **syntax:array** | | static array literal | `int[2] a = [1,2];` | `var a = [1,2]` | | dynamic array literal | `auto a = [1,2];` | `var a = @[1,2]` | | dynamic array create | `auto a = new int[2];` | `var a = newSeq[int](2)` | | empty dynamic array | `auto a = [];` | `var a:seq[int] = @[]` | | indexing slice of a | `a[1..$], a[1..$-1], a[1..3]` | `a[1..^1], a[1..^2], a[1..<3]` | | length | `a.length;` | `a.len` | | **types** | | initial value of type | `T.init` (known at CT) | ? | | type of | `typeof(expr)` | `expr.type` | | type name | `T.stringof` (builtin) | `T.name` (import typetraits) | | class | `class A : B` | `type A = ref object of B` | | struct | `struct A` | `type A = object` | | float: 32, 64 bit | float, double | float32, float(float64) | | pointer sized int, uint | ptrdiff_t, size_t | int, uint | | sized ints | byte, short, int, long | int8, int16, int32, int64 | | char types | char, wchar, dchar | ?,?,? | | **functions** | | delegates | `int delegate(int, int)` | `proc (a, b: int): int {.closure.}` | | **decl** | | variable decl | `auto a=foo;` | `var a=foo` | | immutable decl | `immutable foo=bar;` | `let foo=bar` | | compile time decl | `enum foo=bar;` | `const foo=bar` | | shared static | `__gshared a = 0;` | `var a {.global.} = 0` ? | | static TLS | `static a = 0;` | `var a {.threadvar.} : int` | | ref return | `auto ref fun(ref A a){ return a.x;}` | `proc fun(a:var A):var a.x.type = return a.x` | | skip initialization | `T a = void;` | `var a {.noInit.}: T` | | **attributes** | | purity | `pure` | `{.noSideEffect.}` | | nothrow | `nothrow` | `{.raises: [].}` | | safe | `@safe` | ? | | GC free | `@nogc` | ? | | lockfree | ? | `{.locks:0.}` | | **language** | | unit tests | `unittest{stmt}` | https://nim-lang.org/docs/unittest.html | | constructor | `T(args)` | ad-hoc: `newT(args)`, but see https://github.com/nim-lang/Nim/issues/7474 | | **semantics** | | float.init | NaN | 0 | | file | `__FILE__` | instantiationInfo; limitation: doesn't work for function caller, cf https://github.com/nim-lang/Nim/issues/7406 | | **traits** | | does expr compile | `__traits(compiles, expr)` | `compiles(expr)` | | get fields | `T.tupleof` | `x.fields` | | **metaprogramming** | | static assert | `static assert(foo);` | `static: assert foo` | | **library** | | universal type conversion | a.to!T | no: https://github.com/nim-lang/Nim/issues/7430 | | option/maybe type | [`Nullable`](https://dlang.org/phobos/std_typecons.html#.Nullable) in `std.typecons` | [`Option`](https://nim-lang.org/docs/options.html) in the `options` module | | path append | a.buildPath(b) | a / b ; does right thing on windows; NOTE: if b is absolute, buildPath returns b unlike nim | | **cmd line** | | custom define | -version=foo | --define:foo or --define:foo=bar | | **resources** | | tutorials | https://tour.dlang.org/ | https://nim-lang.org/docs/tut1.html | | **tools** | | caching compiler | rdmd | nim (compiles dependencies; only compiles changed file by default); https://github.com/Jeff-Ciesielski/nimr (subset of functionality) | | find declaration | dscanner --declaration | nimgrep (but no declaration search, cf https://github.com/nim-lang/Nim/issues/7419) | | fix code | dfix | nimfix | | package manager | dub | nimble | | install specific compiler versions | digger | choosenim |

See also libraries.md

nim questions (besides entries marked above with ?)

nim questions (answered)

proc main() { echo “Hello” }

when (isMainModule) { main() } ```

no longer valid points

https://forum.nim-lang.org/t/1779/1#11314 => dmd backend license was changed recently