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 #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 nim-lang/Nim#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 nim-lang/Nim#5287 | 1 |
User defined operators | partial: opCall opSlice, opAssign etc | yes | -1 |
User defined attributes | yes | ? | ? |
RAII | yes (modulo caveats #27) | no, see: RAII | 1 |
prevention of null dereferencing | manual null checks required
|
manual null checks required
Nim also has an experimental |
? |
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 #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 nim-lang/Nim#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 nim-lang/Nim#3837, nim-lang/Nim#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: nim-lang/Nim#7420 |
1 |
code reduction for bugs | dustmite | pending nim-lang/Nim#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 nfrom 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
https://rawgit.com/wilzbach/state-of-d/master/report.html) that are already supported in Nim
features requested in latest D survey (- From the D survey question "What language features do you miss?", these features are supported in Nim: tuples, named arguments, string interpolation, in-place struct initialization, UFCS for local symbols, writing files at compile-time
- these are doable in library code: assert diagnostics, static break
- these are sort of available: Static inheritance
compilation time
- https://forum.nim-lang.org/t/1372
- tcc instead of clang (but not on OSX, see wheineman/nrpl#16)
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 #11
similar code comparison
differences (not clear if pro or con)
- nim has head immutability unlike D: http://nim-by-example.github.io/types/objects/
- Indentation based syntax, but supports syntax skins
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 nim-lang/Nim#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");} |
try: raise newException(IOError, "test exception") |
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 nim-lang/Nim#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 in std.typecons |
Option 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 nim-lang/Nim#7419) |
fix code | dfix | nimfix |
package manager | dub | nimble |
install specific compiler versions | digger | choosenim |
See also libraries.md
links
- https://www.slant.co/versus/118/395/~d_vs_nim
- https://forum.nim-lang.org/t/1779 Nim vs D
- https://forum.dlang.org/post/mailman.1536.1428946094.3111.digitalmars-d@puremagic.com D vs nim
- http://gradha.github.io/articles/2015/02/goodbye-nim-and-good-luck.html
- https://www.quora.com/Of-the-emerging-systems-languages-Rust-D-Go-and-Nim-which-is-the-strongest-language-and-why
- https://github.com/kostya/benchmarks
- https://digitalmars.com/d/archives/digitalmars/D/D_and_Nim_251571.html
- https://forum.nim-lang.org/t/1983#12391 A few questions about Nim
?
)
nim questions (besides entries marked above with -
does it have dfmt equivalent?
-
instead of
newSeq
could we writenew!Seq
or new[Seq] or anything else that's generic and doesn't pollute namespace? -
how to use dirty templates/macros that require an import? eg:
template foo*() {.dirty.} { #[want: import os ]# let programName = getAppFilename() }
-
how to compose arbitrary ranges/iterators in Nim?
-
how to define a slice of type T from ptr + length? cf nim-lang/Nim#5437 feature-request pointer size pair, (openArray as value) #5437
nim questions (answered)
- are there real immutable variables in nim ? A: https://www.reddit.com/r/nim/comments/2w32oi/immutability_in_nim/
- how to specify immutable inside
for(foo in bar)
? A: immutability depends on iterator and by default iterators return immutable values.
var a = @[1, 2, 3, 4]
for item in a: item += 5 # compile-time error
for item in mitems(a): item += 5 # ok
-
how to search for symbols
fooBar
given that nim allows foobar fooBar foo_bar etc? A: nimgrep; also: Nim provides "nimsuggest" that can be used for vim, emacs, vscode, atom that autocompletes -
how to use global variables? (cf http://gradha.github.io/articles/2015/02/goodbye-nim-and-good-luck.html) A: Non-const global vars are not allowed in proc tagged noSideEffects and care must be used if accessed from multiple threads but there is nothing special about them otherwise.
-
can we modify a slice of an immutable array declared by 'let'? A: Slicing doesn't return a var T for let arrays so you can't. Note that immutability for stack objects/array/types is deep but for ref/ptr types it is shallow, it means that the memory address pointed to cannot be modified but the content can.
-
is there a robust way to write complex nim functions as a one liner (eg, for use in 1 line docs, in REPL etc); ideally by replacing indentation with braces A:
(st1; st2; st3)
, or undocumented braces syntax skin:
#? braces
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