#+title: Nim #+date: 2018-05-17 #+setupfile: ../scripter-setupfile.org #+seq_todo: NEED__TO__UNDERSTAND | DONE #+seq_todo: TO__BE__FIXED | FIXED #+hugo_auto_set_lastmod: t #+hugo_base_dir: ../../ #+hugo_section: notes #+hugo_bundle: nim #+export_file_name: index #+hugo_categories: programming #+hugo_tags: nim examples #+hugo_custom_front_matter: :versions '((nim . "6620b5dc8d2")) #+startup: shrink #+begin_description Collection of Nim snippets with brief notes as I try them out from the [[https://nim-lang.org/docs/tut1.html][official Nim tutorial]], [[https://nim-by-example.github.io/][Nim by Example]] and many other places. This also includes my own musings and hard-learned lessons. #+end_description {{{update(<2018-09-27 Thu>,Verified all the code snippets for Nim 0.19.0)}}} * Nim Help If using Nim 0.19.0 or newer, use ~nim --fullhelp~[fn:1] --- don't use ~nim --help~. I ended up with above conclusion as I needed the =--out:= functionality but couldn't find it in the default help (~--help~). /I don't see why anyone would ever use the less informative ~--help~ over ~--fullhelp~./ If people are complaining about "too much information in help", they should use it the /right way/.. pipe the help to ~grep~ or [[https://github.com/BurntSushi/ripgrep][~rg~]]. #+begin_src shell :eval no > nim --fullhelp | rg out -o:FILE, --out:FILE set the output filename --stdout output to stdout in the generated output #+end_src * Echo ** Nim version #+begin_src nim assert NimVersion is string echo NimVersion assert NimMajor is int echo NimMajor assert NimMinor is int echo NimMinor assert NimPatch is int echo NimPatch #+end_src #+RESULTS: : 0.19.0 : 0 : 19 : 0 /See [[#check-type]] for ~is~./ ** Echo with newlines The ~echo~ proc prints to the /stdout/ with a newline added at the end. #+begin_src nim echo("Hello World") #+end_src #+results: : Hello World The parentheses are optional. #+begin_src nim echo "Hello World" #+end_src #+results: : Hello World *** Echo with multiple arguments From [[https://nim-lang.org/docs/system.html#echo,varargs%5Btyped,%5D][Nim Docs -- ~echo~]]: #+begin_quote #+begin_src nim :eval no proc echo(x: varargs[typed, `$`]) {..} #+end_src #+end_quote It means that ~echo~ accepts multiple arguments, and all of them are /stringified/ using the ~$~ proc. ~echo~ outputs each of those stringified arguments on the /stdout/ one after another and appends a newline at the very end. So the ~echo~ statements in the below snippet have the same outputs: #+begin_src nim # First concatenating the strings and passing as single arg to echo echo $100 & "abc" & $2.5 # Passing multiple args to echo, and letting echo "concatenate" them echo 100, "abc", 2.5 #+end_src #+RESULTS: : 100abc2.5 : 100abc2.5 *** ~writeLine~ Another way to print to the /stdout/ is to use the ~writeLine~ proc, but by passing the ~stdout~ argument to the /File/ parameter. #+begin_src nim stdout.writeLine "Hello World!" stdout.writeLine "Hello World again!!" #+end_src #+RESULTS: : Hello World! : Hello World again!! ** Echo without newlines #+begin_src nim stdout.write("Hello") stdout.write("World") #+end_src #+RESULTS: : HelloWorld ** Colored or Styled echo Use ~styledEcho~ template from the ~terminal~ module (which is syntactic sugar for ~stdout.styledWriteLine~ from that same module). The syntax looks like this: #+begin_example styledEcho , , , resetStyle, , , , resetStyle #+end_example As shown above (~, ,~), you can stack multiple "styles" together if they make sense.. like ~styleBright, fgBlue,~. *** Available Styles **** Style #+begin_src nim :eval no type Style* = enum ## different styles for text output styleBright = 1, ## bright text styleDim, ## dim text styleItalic, ## italic (or reverse on terminals not supporting) styleUnderscore, ## underscored text styleBlink, ## blinking/bold text styleBlinkRapid, ## rapid blinking/bold text (not widely supported) styleReverse, ## reverse styleHidden, ## hidden text styleStrikethrough ## strikethrough #+end_src **** Foreground Color #+begin_src nim :eval no type ForegroundColor = enum fgBlack = 30, ## black fgRed, ## red fgGreen, ## green fgYellow, ## yellow fgBlue, ## blue fgMagenta, ## magenta fgCyan, ## cyan fgWhite ## white #+end_src **** Background Color #+begin_src nim :eval no type BackgroundColor = enum bgBlack = 40, ## black bgRed, ## red bgGreen, ## green bgYellow, ## yellow bgBlue, ## blue bgMagenta, ## magenta bgCyan, ## cyan bgWhite ## white #+end_src **** Other #+begin_src nim :eval no type TerminalCmd = enum resetStyle, ## reset attributes fgColor, ## set foreground's true color bgColor ## set background's true color #+end_src *** ~styledEcho~ Example #+begin_src nim :eval no import terminal styledEcho "unstyled text", styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!" styledEcho "unstyled text", styleBright, fgRed, "[FAIL]", resetStyle, fgRed, " Nay :(" #+end_src #+RESULTS: : unstyled text^[[1m^[[32m[PASS]^[[0m^[[32m Yay!^[[0m : unstyled text^[[1m^[[31m[FAIL]^[[0m^[[31m Nay :(^[[0m /The binary control char is manually replaced with ASCII "^[" in the above results block and more below. That's so that they are visible in the Markdown/HTML, and the Org file also remains "ascii-diffable"/. As you see above, ~styledEcho~ automatically adds the ANSI code for resetting the style (~^[[0m~) at the end of the line, *before* adding the trailing newline. *** Echoing styled text without trailing newline Below snippet shows the use of ~stdout.styledWrite~ to echo strings of different styles (includes changing the foreground and background colors) on the same line. #+name: code__styledWrite #+caption: Using ~stdout.styledWrite~ to echo styled string without trailing newline #+begin_src nim :eval no import terminal stdout.styledWrite(fgRed, "red text ") # no newline here stdout.styledWrite(fgGreen, "green text ") # no newline here stdout.styledWrite(fgBlue, "blue text ") # no newline here stdout.styledWrite(fgYellow, "yellow text ") # no newline here stdout.styledWrite("ordinary text ") # no newline here stdout.styledWrite(fgCyan, "cyan text ") # no newline here echo "" #+end_src #+RESULTS: code__styledWrite : ^[[31mred text ^[[0m^[[32mgreen text ^[[0m^[[34mblue text ^[[0m^[[33myellow text ^[[0mordinary text ^[[36mcyan text ^[[0m *** Lower level styling procs/templates - ~setForeGroundColor~ :: Sets foreground color for the /stdout/ output that follows. /It is the same as calling ~stdout.setForeGroundColor~./ - ~setBackGroundColor~ :: Sets background color for the /stdout/ output that follows. /It is the same as calling ~stdout.setBackGroundColor~./ - ~resetAttributes~ :: Resets the styling/coloring. /It is the same as calling ~stdout.resetAttributes~./ #+begin_src nim :eval no import terminal setForeGroundColor(fgGreen) echo "green text" echo "more green text" setForeGroundColor(fgRed) echo "red text" resetAttributes() echo "ordinary text" #+end_src #+RESULTS: : ^[[32mgreen text : more green text : ^[[31mred text : ^[[0mordinary text Below code snippet is similar to the snippet in [[code__styledWrite]], except that the low-level procs ~setForeGroundColor~, ~resetAttributes~ and ~stdout.write~ are used instead of ~stdout.styledWrite~. #+begin_src nim :eval no import terminal setForeGroundColor(fgRed) stdout.write("red text ") # no newline here setForeGroundColor(fgGreen) stdout.write("green text ") # no newline here setForeGroundColor(fgBlue) stdout.write("blue text ") # no newline here setForeGroundColor(fgYellow) stdout.write("yellow text ") # no newline here resetAttributes() stdout.write("ordinary text ") # no newline here setForeGroundColor(fgCyan) stdout.write("cyan text ") # no newline here resetAttributes() echo "" #+end_src #+RESULTS: : ^[[31mred text ^[[32mgreen text ^[[34mblue text ^[[33myellow text ^[[0mordinary text ^[[36mcyan text ^[[0m ** Standard Error :PROPERTIES: :CUSTOM_ID: stderr :END: To send message on /stderr/, use ~writeLine~ proc to explicitly write to ~stderr~ /File/. #+begin_src nim :eval no stderr.writeLine "Error!" quit 1 #+end_src Above will generate this error: #+begin_example Error! #+end_example To send messages to ~stderr~ that don't end in newlines, use ~stderr.write~ instead. #+begin_src nim :eval no stderr.write "Error1" stderr.write "Error2" quit 1 #+end_src Above will generate this error: #+begin_example Error1Error2 #+end_example ** NEED__TO__UNDERSTAND When to use ~stdout.flushFile~? * Syntax ** Line continuation Nim does not have any line-continuation character -- a character you can use to break a long line of code. #+begin_note Instead /line continuation/ is inferred if a line ends in an *operator*, ~,~, or any of these opening brackets (~(~, ~[~, ~{~). #+end_note See the below code. It works, but it has a really long line of code. #+begin_src nim proc isTriangle(s: openArray[int]): bool = return (s.len == 3) and (s[0] <= s[1] + s[2]) and (s[1] <= s[2] + s[0]) and (s[2] <= s[0] + s[1]) and (s[0] > 0) and (s[1] > 0) and (s[2] > 0) echo isTriangle([3, 3, 3]) #+end_src #+RESULTS: : true Below will not work as the /continued lines/ are not ending with an operator -- You will get /Error: invalid indentation/. /I really wish this worked!/ #+begin_src nim :eval no proc isTriangle(s: openArray[int]): bool = return (s.len == 3) and (s[0] <= s[1] + s[2]) and (s[1] <= s[2] + s[0]) and (s[2] <= s[0] + s[1]) and (s[0] > 0) and (s[1] > 0) and (s[2] > 0) echo isTriangle([3, 3, 3]) #+end_src Below, I am ending the /continued lines/ with the ~and~ operator. But that will fail with the same error too.. because I am *not indenting* the /continued lines/. #+begin_src nim :eval no proc isTriangle(s: openArray[int]): bool = return (s.len == 3) and (s[0] <= s[1] + s[2]) and (s[1] <= s[2] + s[0]) and (s[2] <= s[0] + s[1]) and (s[0] > 0) and (s[1] > 0) and (s[2] > 0) echo isTriangle([3, 3, 3]) #+end_src Finally, below works because: - The /continued lines/ are ending with an *operator* (~and~ in this example). - When continued, they resume after an *indentation*. #+begin_src nim proc isTriangle(s: openArray[int]): bool = return (s.len == 3) and (s[0] <= s[1] + s[2]) and (s[1] <= s[2] + s[0]) and (s[2] <= s[0] + s[1]) and (s[0] > 0) and (s[1] > 0) and (s[2] > 0) echo isTriangle([3, 3, 3]) #+end_src #+RESULTS: : true [[https://github.com/nim-lang/Nim/wiki/Whitespace-FAQ#new-lines][Ref]] * Templates and Macros ** TODO Templates You can pass a block of statements as a last argument to a template call following the special ~:~ as shown below: #+begin_src nim template foo(body: untyped) = body foo(): # special colon echo "hello" echo "world" #+end_src #+RESULTS: : hello : world *** DONE Return type of templates CLOSED: [2018-06-27 Wed 11:40] Below template has a return type of "void" and it still is returning a literal integer 1. So you get an error. #+begin_src nim :eval no template foo() = 1 echo foo() #+end_src #+begin_example nim_src_co9l9c.nim(6, 9) Error: expression '1' is of type 'int literal(1)' and has to be discarded #+end_example To fix that, you need to assign the correct return type for the ~foo~ template: #+begin_src nim template foo(): int = 1 echo foo() #+end_src #+RESULTS: : 1 Or set it to ~untyped~ so that the template replacement is done before any semantic checking or type resolution. #+begin_src nim template foo(): untyped = 1 echo foo() #+end_src #+RESULTS: : 1 Thanks to {{{guser(mratsim)}}} for [[https://gitter.im/nim-lang/Nim?at=5b33a7f26ceffe4eba37e9d2][this reply]] to my question regarding ~untyped~ return type for templates: #+begin_quote ~untyped~ is useful to make sure the replacement is done before any semantic checking/type resolution. #+end_quote If you are getting confused about whether or not to set a template's return type or what to set it to, just set it to ~untyped~. #+begin_note If in doubt, set a template's return type as ~untyped~. #+end_note **** COMMENT In https://nim-lang.org/docs/manual.html#templates-passing-a-code-block-to-a-template, the first example has return type as ~untyped~, while the second example has no return type. When do you need to set the return type of templates as ~untyped~? ** NEED__TO__UNDERSTAND Macros - Comment by {{{guser(Vindaar)}}} on fixing the macro [[https://github.com/nim-lang/Nim/pull/8047#issuecomment-397676285][here]]. - His [[https://gitter.im/nim-lang/Nim?at=5b23f3d0d128fa71f66cf442][further response]] on why ~result[^1].add(arg)~ was used instead of ~result.add(arg)~. *** COMMENT Macro Example [[https://gitter.im/nim-lang/Nim?at=5b33dcb56ceffe4eba3888ad][Ref]] #+begin_src nim import macros, strformat, strutils macro checkEqual(value, compareTo: typed): untyped = #result = newNimNode(nnkStmtList, value) let varName = $value let compareName = $toStrLit(compareTo) let fmtString = "$1 == $2? {$1 == $2}" % [varName, compareName] result = newNimNode(nnkStmtList, value) result.add newTree( nnkCall, newIdentNode("echo"), nnkPrefix.newTree( newIdentNode("&"), newLit(fmtString) ) ) let x = 42 checkEqual(x, 42) #+end_src #+RESULTS: : x == 42? true *** NEED__TO__UNDERSTAND Quote Do - [[https://gitter.im/nim-lang/Nim?at=5b969a18728ddf02828a4068][Ref]] **** Example of ~quote do~ [[http://ix.io/1mpu/nim][Ref]] by {{{guser(Vindaar)}}} from [[https://gitter.im/nim-lang/Nim?at=5b96a04551a02e2a260d8053][here]]. /Once I understand Nim macros and ~quote do~, I need to revisit this example to understand the ~quote do~ magic. - <2018-09-10 Mon>/ #+begin_src nim import macros type ShapeKind {.pure.} = enum Triangle, Rectangle Shape = object case kind: ShapeKind of Triangle: aT: int bT: int of Rectangle: aR: int bR: int proc calcArea(s: Shape) = discard proc createNext(c: NimNode): NimNode = let typ = c[0] let name = c[1] let a = c[2][0][0] let aVal = c[2][0][1] let b = c[2][1][0] let bVal = c[2][1][1] case $typ of "Triangle": a.ident = toNimIdent($a & "T") b.ident = toNimIdent($b & "T") of "Rectangle": a.ident = toNimIdent($a & "R") b.ident = toNimIdent($b & "R") result = quote do: let `name` = Shape(kind: `typ`, `a`: `aVal`, `b`: `bVal`) `name`.calcArea() macro shape(input: untyped): untyped = ## should produce ## let aaa = Shape(kind: Triangle, a: 17, b: 23) ## aaa.calcArea() ## let bbb = Shape(kind: Rectangle, a: 5, b: 8) ## bbb.calcArea() result = newStmtList() echo input.treeRepr for i in 0 ..< input.len: let c = input[i] case c.kind of nnkCommand: result.add createNext(c) else: echo "Needs to be a Command!" echo result.repr shape: Triangle aaa: a = 17 b = 23 Rectangle bbb: a = 5 b = 8 #+end_src #+RESULTS: *** TODO Quote Helper [[https://nim-lang.org/docs/macros.html#quote,typed,string][Nim Docs -- Macros / ~quote~]] **** COMMENT Quote Helper Example #+begin_src nim import macros, strformat, strutils macro checkEqual(value, compareTo: typed): untyped = let fmtString = "$1 == $2? {$1 == $2}" % [$value, $toStrLit(compareTo)] result = quote do: echo &`fmtString` let x = 42 checkEqual(x, 42) #+end_src #+RESULTS: : x == 42? true ** TODO Term Rewriting Macros [[https://nim-lang.org/docs/manual.html#term-rewriting-macros][Nim Manual -- Term Rewriting Macros]] Term rewriting macros (TRM) are macros or templates that have not only a /name/ but also a /pattern/ that is searched for after the semantic checking phase of the compiler. This means they provide an easy way to enhance the compilation pipeline with user defined optimizations. #+begin_src nim import typetraits, strformat template optMul{`*`(a, 2)}(a: int): int = debugEcho("-> Term rewriting activated!") a + a echo fmt"First arg = 3 ({$3.type})" echo "Calculating 3 * 2:" # Why isn't term rewriting activated here? echo 3 * 2 let x = 3 echo "" echo fmt"First arg = x ({$x.type})" echo "Calculating x * 2:" echo x * 2 echo "" echo fmt"First arg = 2 ({$2.type})" echo "Calculating 2 * x:" echo 2 * x #+end_src #+RESULTS: #+begin_example First arg = 3 (int) Calculating 3 * 2: 6 First arg = x (int) Calculating x * 2: -> Term rewriting activated! 6 First arg = 2 (int) Calculating 2 * x: 6 #+end_example Above, the compiler now rewrites ~x * 2~ as ~x + x~. The code inside the curlies (~{`*`(a, 2)}~) is the pattern to match against. The operators ~*~, ~**~, ~|~, ~~~ (see [[https://nim-lang.org/docs/manual.html#term-rewriting-macros-pattern-operators][TRM /pattern operators/]]) have a special meaning in patterns if they are written in infix notation. So to match verbatim against ~*~ the ordinary function call syntax needs to be used. *** TO__BE__FIXED TRM Side Effects Unfortunately optimizations are hard to get right. See the below tiny example that doesn't work as expected. It's causing the side effects in ~f~ proc to repeat too! #+begin_src nim template optMul{`*`(a, 2)}(a: int): int = debugEcho("-> Term rewriting activated!") a + a proc f(): int = echo "side effect!" result = 55 echo f() * 2 #+end_src #+RESULTS: : -> Term rewriting activated! : side effect! : side effect! : 110 We *should not* duplicate ~a~ (the ~a~ parameter in that ~optMul~ TRM) if it denotes an expression that has a side effect! Fortunately Nim /knows/ whether or not side effects are there. So with the below, the TRM should not be getting activated if creating ~a~ has side effects. /Though, as of <2018-10-10 Wed>, the ~noSideEffect~ seems to have no .. effect. See {{{nimissue(6217)}}}./ #+begin_src nim template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = debugEcho("-> Term rewriting activated!") a + a proc f(): int = echo "side effect!" result = 55 echo f() * 2 # not optimized ;-) #+end_src #+RESULTS: : -> Term rewriting activated! : side effect! : side effect! : 110 *** TO__BE__FIXED TRM Commutative You can make one overload matching with a constraint and one without, and the one with a constraint will have precedence, and so you can handle both cases differently. So what about ~2 * a~? We should tell the compiler ~*~ is commutative. We cannot really do that however as the following code only swaps arguments blindly. Below code will cause the TRM to be executed exactly (odd) 36 times, and then for later multiplications, the TRM doesn't get activated at all! (see {{{nimissue(9288)}}}): #+begin_src nim template mulIsCommutative{`*`(a, b)}(a, b: int): int = debugEcho("-> Term rewriting activated!") b * a let x = 3 echo "Calculating x * 2:" echo x * 2 echo "" echo "Calculating x * 2 again:" echo x * 2 echo "" echo "Calculating x * 3:" echo x * 3 #+end_src #+RESULTS: #+begin_example Calculating x * 2: -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! -> Term rewriting activated! 6 Calculating x * 2 again: 6 Calculating x * 3: 9 #+end_example *** TRM Parameter Constraints The Nim Manual says: #+begin_quote What optimizers really need to do is a /canonicalization/. #+end_quote I think that "canonicalization" means, make the TRM arg types /less generic/, so that that infloop can be prevented. So by limiting the first arg to be a *literal* int, the ~canonMul~ TRM will be activated only if the first arg *is* a literal int (like 3, and not when it's a variable holding /int 3/). #+begin_src nim template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = debugEcho("-> Term rewriting activated!") b * a let x = 3 y = 4 echo "The TRM won't be activated for 'x * y' because 'x' is not a literal int:" echo x * y echo "" echo "The TRM will activated for '3 * y' because '3' *is* a literal int:" echo 3 * y #+end_src #+RESULTS: : The TRM won't be activated for 'x * y' because 'x' is not a literal int: : 12 : : The TRM will activated for '3 * y' because '3' *is* a literal int: : -> Term rewriting activated! : 12 The ~int{lit}~ parameter pattern matches against an expression of type ~int~, but only if it's a *literal*. #+begin_note ~lit~ is just one of the [[https://nim-lang.org/docs/manual.html#term-rewriting-macros-parameter-constraints][*many* available TRM /parameter constraints/]]! #+end_note * Representing one type in another #+attr_html: :class repr-type wide-table #+attr_css: :font-size 0.7em #+name: tab__representing_one_type_in_another #+caption: Representing one type in another |---------------------+----------------------------------------------------+-----------------------------------------------------------------+---------------------------------+-----------------------+--------------------------------------------------------------| | From Type / To Type | *bool* | *char* | *int* | *float* | *string* | |---------------------+----------------------------------------------------+-----------------------------------------------------------------+---------------------------------+-----------------------+--------------------------------------------------------------| | *bool* | - | N/A | ~int~ | ~float~ | ~$~ or [[https://devdocs.io/nim/strformat][~strformat.fmt~]] or /use plain logic to return string/ | | *char* | N/A | - | ~ord~ (or ~int~) | ~float~ | ~$~ or [[https://devdocs.io/nim/strformat][~strformat.fmt~]] or /custom logic using ~newString~/ | | *int* | ~float.bool~ (or ~bool~, /int needs to be 0 or 1/) | ~float.char~ (or [[https://devdocs.io/nim/system#chr,range][~chr~]] or ~char~, /int needs to be in [0,255]/) | - | ~float~ | ~$~ or [[https://devdocs.io/nim/strformat][~strformat.fmt~]] or [[https://devdocs.io/nim/strutils#intToStr,int,Positive][~strutils.intToStr~]] | | *float* | ~bool~ | ~char~ /(truncation + rollover)/ | ~int~ /(truncation + rollover)/ | - | ~$~ or [[https://devdocs.io/nim/strformat][~strformat.fmt~]] | | *string* | [[https://devdocs.io/nim/strutils#parseBool,string][~strutils.parseBool~]] | /as a ~seq~ of ~char~/ | [[https://devdocs.io/nim/parseutils#parseInt,string,int,int][~strutils.parseInt~]] | [[https://devdocs.io/nim/strutils#parseFloat,string][~strutils.parseFloat~]] | - | |---------------------+----------------------------------------------------+-----------------------------------------------------------------+---------------------------------+-----------------------+--------------------------------------------------------------| ** From ~bool~ *** ~bool~ to ~char~ /not applicable/ *** DONE ~bool~ to ~int~ CLOSED: [2018-05-30 Wed 11:44] #+begin_src nim import strformat, typetraits let b_seq = @[true, false] for b in b_seq: var b_int = b.int echo fmt"Value of {b.type.name} b is {b}" echo fmt"Value of {b_int.type.name} b_int is {b_int}" #+end_src #+RESULTS: : Value of bool b is true : Value of int b_int is 1 : Value of bool b is false : Value of int b_int is 0 *** DONE ~bool~ to ~float~ CLOSED: [2018-05-30 Wed 11:44] #+begin_src nim import strformat, typetraits let b_seq = @[true, false] for b in b_seq: var b_float = b.float echo fmt"Value of {b.type.name} b is {b}" echo fmt"Value of {b_float.type.name} b_float is {b_float}" #+end_src #+RESULTS: : Value of bool b is true : Value of float b_float is 1.0 : Value of bool b is false : Value of float b_float is 0.0 *** DONE ~bool~ to ~string~ CLOSED: [2018-05-30 Wed 11:54] #+begin_src nim import strformat, typetraits let b_seq = @[true, false] for b in b_seq: var b_string1 = $b var b_string2 = fmt"{b}" var b_string3 = if b: "true" else: "false" echo fmt"Value of {b.type.name} b is {b}" echo fmt" Value of {b_string1.type.name} b_string1 is {b_string1}" echo fmt" Value of {b_string2.type.name} b_string2 is {b_string2}" echo fmt" Value of {b_string3.type.name} b_string3 is {b_string3}" #+end_src #+RESULTS: : Value of bool b is true : Value of string b_string1 is true : Value of string b_string2 is true : Value of string b_string3 is true : Value of bool b is false : Value of string b_string1 is false : Value of string b_string2 is false : Value of string b_string3 is false ** From ~char~ *** ~char~ to ~bool~ /not applicable/ *** DONE ~char~ to ~int~ CLOSED: [2018-05-30 Wed 11:13] #+begin_src nim import strformat, typetraits let c = 'A' c_int1 = c.ord c_int2 = c.int echo fmt"Value of {c.type.name} c is {repr(c)}" echo fmt" Value of {c_int1.type.name} c_int1 is {c_int1}" echo fmt" Value of {c_int2.type.name} c_int2 is {c_int2}" #+end_src #+RESULTS: : Value of char c is 'A' : Value of int c_int1 is 65 : Value of int c_int2 is 65 As ~char~ is an [[#ordinals][Ordinal]] type, [[https://nim-lang.org/docs/system.html#ord,T][~ord~]] is very suitable for converting a /char/ to /int/. *** DONE ~char~ to ~float~ CLOSED: [2018-05-30 Wed 11:14] #+begin_src nim import strformat, typetraits let c = 'A' c_float = c.float echo fmt"Value of {c.type.name} c is {repr(c)}" echo fmt"Value of {c_float.type.name} c_float is {c_float}" #+end_src #+RESULTS: : Value of char c is 'A' : Value of float c_float is 65.0 *** DONE ~char~ to ~string~ CLOSED: [2018-05-30 Wed 12:00] #+begin_src nim import strformat, typetraits from strutils import join let c_seq = @['A', 'b', '@'] for c in c_seq: let c_string1 = $c c_string2 = fmt"{c}" c_string3 = @[c].join("") var c_string4 = newString(1) c_string4[0] = c echo fmt"Value of {c.type.name} c is {repr(c)}" echo fmt" Value of {c_string1.type.name} c_string1 is {c_string1}" echo fmt" Value of {c_string2.type.name} c_string2 is {c_string2}" echo fmt" Value of {c_string3.type.name} c_string3 is {c_string3}" echo fmt" Value of {c_string4.type.name} c_string4 is {c_string4}" #+end_src #+RESULTS: #+begin_example Value of char c is 'A' Value of string c_string1 is A Value of string c_string2 is A Value of string c_string3 is A Value of string c_string4 is A Value of char c is 'b' Value of string c_string1 is b Value of string c_string2 is b Value of string c_string3 is b Value of string c_string4 is b Value of char c is '@' Value of string c_string1 is @ Value of string c_string2 is @ Value of string c_string3 is @ Value of string c_string4 is @ #+end_example **** Join a sequence of characters into a string #+begin_src nim import strformat, typetraits from strutils import join let c_seq = @['a', 'b', 'c', 'd'] str = c_seq.join("") echo fmt"{str} is the stringified form of {c_seq.type.name} {c_seq}" #+end_src #+RESULTS: : abcd is the stringified form of seq[char] @['a', 'b', 'c', 'd'] ** From ~int~ *** DONE ~int~ to ~bool~ CLOSED: [2018-05-30 Wed 11:48] - The int value needs to be either 0 or 1. - /RangeError/ exception is thrown for any other int value. #+begin_src nim import strformat, typetraits let i_seq = @[-1, 0, 1, 2] for i in i_seq: echo fmt"Value of {i.type.name} i is {i}" var i_bool: bool try: i_bool = i.bool echo fmt" Value of {i_bool.type.name} i_bool is {i_bool}" except: echo fmt" [Error] {getCurrentException().name}: {getCurrentException().msg}" #+end_src #+RESULTS: : Value of int i is -1 : [Error] RangeError: value out of range: -1 : Value of int i is 0 : Value of bool i_bool is false : Value of int i is 1 : Value of bool i_bool is true : Value of int i is 2 : [Error] RangeError: value out of range: 2 One way to get around this limitation on int values is to first convert it to a ~float~ [[#float-to-bool][and then to a =bool=]]. With this, any non-zero value of the int will return ~true~, and only the value of 0 will return ~false~. #+begin_src nim import strformat, typetraits let i_seq = @[-1000, 0, 1, 1000] for i in i_seq: echo fmt"Value of {i.type.name} i is {i}" var i_bool: bool try: i_bool = i.float.bool echo fmt" Value of {i_bool.type.name} i_bool is {i_bool}" except: echo fmt" [Error] {getCurrentException().name}: {getCurrentException().msg}" #+end_src #+RESULTS: : Value of int i is -1000 : Value of bool i_bool is true : Value of int i is 0 : Value of bool i_bool is false : Value of int i is 1 : Value of bool i_bool is true : Value of int i is 1000 : Value of bool i_bool is true *** DONE ~int~ to ~char~ CLOSED: [2018-05-30 Wed 11:05] - The int value needs to be in the ~[0, 255]~ range. - /RangeError/ exception is thrown if that value is outside that range. #+begin_src nim import strformat, typetraits let i_seq: seq[int] = @[-1, 0, 62, 256] for i in i_seq: var i_char1, i_char2: char echo fmt"Value of {i.type.name} i is {i}" try: i_char1 = chr(i) # or i.chr echo fmt" Value of {i_char1.type.name} i_char1 is {repr(i_char1)}" except: echo fmt" [Error] {getCurrentException().name}: {getCurrentException().msg}" try: i_char2 = i.char # or char(i) echo fmt" Value of {i_char2.type.name} i_char2 is {repr(i_char2)}" except: echo fmt" [Error] {getCurrentException().name}: {getCurrentException().msg}" #+end_src #+RESULTS: #+begin_example Value of int i is -1 [Error] RangeError: value out of range: -1 [Error] RangeError: value out of range: -1 Value of int i is 0 Value of char i_char1 is '\0' Value of char i_char2 is '\0' Value of int i is 62 Value of char i_char1 is '>' Value of char i_char2 is '>' Value of int i is 256 [Error] RangeError: value out of range: 256 [Error] RangeError: value out of range: 256 #+end_example #+begin_note ~chr~ converts only an 8-bit unsigned integer (~range[0 .. 255]~). #+end_note One way to get around this limitation on int values is to first convert it to a ~float~ [[#float-to-char][and then to a =char=]]. With this, any value of int < 0 or >= 256 will returned a rolled-over char value. #+begin_src nim import strformat, typetraits let i_seq = @[-2, -1, 0, 1, 254, 255, 256, 257] for i in i_seq: var i_char = i.float.char echo fmt"Value of {i.type.name} i is {i}" echo fmt" Value of {i_char.type.name} i_char is {repr(i_char)}" #+end_src #+RESULTS: #+begin_example Value of int i is -2 Value of char i_char is '\254' Value of int i is -1 Value of char i_char is '\255' Value of int i is 0 Value of char i_char is '\0' Value of int i is 1 Value of char i_char is '\1' Value of int i is 254 Value of char i_char is '\254' Value of int i is 255 Value of char i_char is '\255' Value of int i is 256 Value of char i_char is '\0' Value of int i is 257 Value of char i_char is '\1' #+end_example *** DONE ~int~ to ~float~ CLOSED: [2018-05-30 Wed 11:17] #+begin_src nim import strformat, typetraits let i_seq: seq[int] = @[-1, 0, 1000] for i in i_seq: var i_float = i.float echo fmt"Value of {i.type.name} i is {i}" echo fmt"Value of {i_float.type.name} i_float is {i_float}" #+end_src #+RESULTS: : Value of int i is -1 : Value of float i_float is -1.0 : Value of int i is 0 : Value of float i_float is 0.0 : Value of int i is 1000 : Value of float i_float is 1000.0 *** DONE ~int~ to ~string~ CLOSED: [2018-05-30 Wed 11:05] #+begin_src nim import strformat, strutils, typetraits let i_seq: seq[int] = @[-1000, 0, 1000] for i in i_seq: echo fmt"Value of {i.type.name} i is {i}" var i_str1 = $i i_str2 = fmt"{i}" i_str3 = intToStr(i) # strutils echo fmt" Value of {i_str1.type.name} i_str1 is {i_str1}" echo fmt" Value of {i_str2.type.name} i_str2 is {i_str2}" echo fmt" Value of {i_str3.type.name} i_str3 is {i_str3}" #+end_src #+RESULTS: #+begin_example Value of int i is -1000 Value of string i_str1 is -1000 Value of string i_str2 is -1000 Value of string i_str3 is -1000 Value of int i is 0 Value of string i_str1 is 0 Value of string i_str2 is 0 Value of string i_str3 is 0 Value of int i is 1000 Value of string i_str1 is 1000 Value of string i_str2 is 1000 Value of string i_str3 is 1000 #+end_example **** Why is even ~intToStr~ needed? As we see above ~$~ provides a very concise way to print integers as strings. On the other hand, ~intToStr~ is verbose and an oddly named function.. the only one named as /fooToBar/ (we don't have ~strToInt~, but ~parseInt~.. I wish it were named as the former). Also, if one wants to use ~intToStr~, they need to first import ~strutils~. From [[https://nim-lang.org/docs/strutils.html#intToStr,int,Positive][~intToStr~ docs]]: #+begin_quote #+begin_src nim :eval no proc intToStr(x: int; minchars: Positive = 1): string {..} #+end_src Converts x to its decimal representation. The resulting string will be minimally minchars characters long. This is achieved by adding leading zeros. #+end_quote So ~intToStr~ would be used to add leading zeros: #+begin_src nim import strutils let i_arr = [-100, -50, 0, 0, 123, 1000] for i in i_arr: echo intToStr(i, 10) #+end_src #+RESULTS: : -0000000100 : -0000000050 : 0000000000 : 0000000000 : 0000000123 : 0000001000 #+begin_note The /minchars/ parameter in ~intToStr~ does not count the negative sign character. #+end_note But then, a similar result also can be achieved by the general formatting ~fmt~ function from ~strformat~. See below to see what I mean by "similar". #+begin_src nim import strformat let i_arr = [-100, -50, 0, 0, 123, 1000] for i in i_arr: echo fmt"{i: 011}" #+end_src #+RESULTS: : -0000000100 : -0000000050 : 0000000000 : 0000000000 : 0000000123 : 0000001000 Breakdown of the " 011" format specifier --- Here is the general ~fmt~ format specifier syntax: #+begin_example [[fill]align][sign][#][0][minimumwidth][.precision][type] #+end_example - The initial space is the '[sign]' part which indicates that space should be used for positive numbers. I used that so that the positive numbers align well (right align) with the negative numbers. - The "0" is the '[0]' part, which 0-pads the numbers on the left. - "11" is the '[minimumwidth]' part which counts the "-" sign character too. The minimum width is set to 11 to match the 10 argument given to the ~intToStr~ in the above example. I prefer the ~fmt~ behavior better. But also, as ~floatToStr~ does not exist, and I would anyways need to resort to ~fmt~ for formatting negative floats, I might as well do the same for negative ints too --- Consistency. So, still.. there isn't a strong case to use ~intToStr~. ** From ~float~ *** DONE ~float~ to ~bool~ CLOSED: [2018-05-30 Wed 11:49] :PROPERTIES: :CUSTOM_ID: float-to-bool :END: - Any ~float~ value between ~(-1.0, 1.0)~ is ~false~. - All other ~float~ values are ~true~. #+begin_src nim import strformat, typetraits let f_seq = @[-1.0, -0.99, -0.1, 0.0, 0.3, 0.5, 0.8, 1.0, 1.1, 2.0] for f in f_seq: var f_bool = f.bool echo fmt"Value of {f.type.name} f is {f}" echo fmt"Value of {f_bool.type.name} f_bool is {f_bool}" #+end_src #+RESULTS: #+begin_example Value of float f is -1.0 Value of bool f_bool is true Value of float f is -0.99 Value of bool f_bool is false Value of float f is -0.1 Value of bool f_bool is false Value of float f is 0.0 Value of bool f_bool is false Value of float f is 0.3 Value of bool f_bool is false Value of float f is 0.5 Value of bool f_bool is false Value of float f is 0.8 Value of bool f_bool is false Value of float f is 1.0 Value of bool f_bool is true Value of float f is 1.1 Value of bool f_bool is true Value of float f is 2.0 Value of bool f_bool is true #+end_example *** DONE ~float~ to ~char~ (truncation + rollover) CLOSED: [2018-05-30 Wed 11:25] :PROPERTIES: :CUSTOM_ID: float-to-char :END: Floats whose truncated integer values are >= 256 or < 0 will roll-over to a char value in the range ~'\0' .. '\255'~ as shown in the below snippet. #+begin_src nim import strformat, typetraits let f_seq: seq[float] = @[-2.0, -1.5, -1.1, 0.0, 0.4, 1.0, 65.0, 65.3, 255.0, 256.0, 257.0] for f in f_seq: var f_char = f.char echo fmt"Value of {f.type.name} f is {f}" echo fmt"Value of {f_char.type.name} f_char is {repr(f_char)}" #+end_src #+RESULTS: #+begin_example Value of float f is -2.0 Value of char f_char is '\254' Value of float f is -1.5 Value of char f_char is '\255' Value of float f is -1.1 Value of char f_char is '\255' Value of float f is 0.0 Value of char f_char is '\0' Value of float f is 0.4 Value of char f_char is '\0' Value of float f is 1.0 Value of char f_char is '\1' Value of float f is 65.0 Value of char f_char is 'A' Value of float f is 65.3 Value of char f_char is 'A' Value of float f is 255.0 Value of char f_char is '\255' Value of float f is 256.0 Value of char f_char is '\0' Value of float f is 257.0 Value of char f_char is '\1' #+end_example *** DONE ~float~ to ~int~ (truncation + rollover) CLOSED: [2018-05-30 Wed 11:20] Note from the below example that rollover happens at extremes of the int values. #+begin_src nim import strformat, typetraits, math let f_seq: seq[float] = @[low(int).float, (low(int)/2).float, -1.9, -1.5, -1.1, 0.0, 0.4, 0.5, 0.9, 1.0, (high(int)/2).float, high(int).float] for f in f_seq: var f_int = f.int echo fmt"Value of {f.type.name} f is {f}" echo fmt"Value of {f_int.type.name} f_int is {f_int}" #+end_src #+RESULTS: #+begin_example Value of float f is -9.223372036854776e+18 Value of int f_int is -9223372036854775808 Value of float f is -4.611686018427388e+18 Value of int f_int is -4611686018427387904 Value of float f is -1.9 Value of int f_int is -1 Value of float f is -1.5 Value of int f_int is -1 Value of float f is -1.1 Value of int f_int is -1 Value of float f is 0.0 Value of int f_int is 0 Value of float f is 0.4 Value of int f_int is 0 Value of float f is 0.5 Value of int f_int is 0 Value of float f is 0.9 Value of int f_int is 0 Value of float f is 1.0 Value of int f_int is 1 Value of float f is 4.611686018427388e+18 Value of int f_int is 4611686018427387904 Value of float f is 9.223372036854776e+18 Value of int f_int is -9223372036854775808 #+end_example *** DONE ~float~ to ~string~ CLOSED: [2018-05-30 Wed 12:18] #+begin_src nim import strformat, typetraits let f_seq: seq[float] = @[-1.9, -1.5, -1.1, 0.0, 0.4, 0.5, 0.9, 1.0] for f in f_seq: var f_string1 = $f f_string2 = fmt"{f}" echo fmt"Value of {f.type.name} f is {f}" echo fmt" Value of {f_string1.type.name} f_string1 is {f_string1}" echo fmt" Value of {f_string2.type.name} f_string2 is {f_string2}" #+end_src #+RESULTS: #+begin_example Value of float f is -1.9 Value of string f_string1 is -1.9 Value of string f_string2 is -1.9 Value of float f is -1.5 Value of string f_string1 is -1.5 Value of string f_string2 is -1.5 Value of float f is -1.1 Value of string f_string1 is -1.1 Value of string f_string2 is -1.1 Value of float f is 0.0 Value of string f_string1 is 0.0 Value of string f_string2 is 0.0 Value of float f is 0.4 Value of string f_string1 is 0.4 Value of string f_string2 is 0.4 Value of float f is 0.5 Value of string f_string1 is 0.5 Value of string f_string2 is 0.5 Value of float f is 0.9 Value of string f_string1 is 0.9 Value of string f_string2 is 0.9 Value of float f is 1.0 Value of string f_string1 is 1.0 Value of string f_string2 is 1.0 #+end_example ** From ~string~ *** DONE ~string~ to ~bool~ CLOSED: [2018-05-30 Wed 11:41] #+begin_src nim import strformat, strutils, typetraits let s_seq = @["true", "True", "tRuE", "false", "False", "FaLsE"] for s in s_seq: var s_bool = parseBool(s) echo fmt"Value of {s.type.name} s is {s}" echo fmt"Value of {s_bool.type.name} s_bool is {s_bool}" #+end_src #+RESULTS: #+begin_example Value of string s is true Value of bool s_bool is true Value of string s is True Value of bool s_bool is true Value of string s is tRuE Value of bool s_bool is true Value of string s is false Value of bool s_bool is false Value of string s is False Value of bool s_bool is false Value of string s is FaLsE Value of bool s_bool is false #+end_example *** DONE ~string~ to ~char~ CLOSED: [2018-05-30 Wed 11:30] A string can be represented as a sequence of chars as shown below. #+begin_src nim import strformat, typetraits let s = "abcd" var c_seq: seq[char] for c in s: c_seq.add(c) echo fmt"Value of {s.type.name} s is {s}" echo fmt"Value of {c_seq.type.name} c_seq is {c_seq}" #+end_src #+RESULTS: : Value of string s is abcd : Value of seq[char] c_seq is @['a', 'b', 'c', 'd'] *** DONE ~string~ to ~int~ CLOSED: [2018-05-30 Wed 11:05] #+begin_src nim import strformat, strutils, typetraits let s = "1212" s_int = parseInt(s) echo fmt"Value of {s.type.name} s is {s}" echo fmt"Value of {s_int.type.name} s_int is {s_int}" #+end_src #+RESULTS: : Value of string s is 1212 : Value of int s_int is 1212 *** DONE ~string~ to ~float~ CLOSED: [2018-05-30 Wed 11:05] #+begin_src nim import strformat, strutils, typetraits let s = "12.12" s_float = parseFloat(s) echo fmt"Value of {s.type.name} s is {s}" echo fmt"Value of {s_float.type.name} s_float is {s_float}" #+end_src #+RESULTS: : Value of string s is 12.12 : Value of float s_float is 12.12 * Data Types ** DONE ~int~ types CLOSED: [2018-06-20 Wed 14:19] :PROPERTIES: :CUSTOM_ID: int-types :END: - int8 :: 8-bit signed integer type - int16 :: 16-bit signed integer type - int32 :: 32-bit signed integer type - int64 :: 64-bit signed integer type - int :: This is the same size as the size of the pointer. So if the code is compiled in 32-bit mode, *int* will be the same size as *int32*, and if it's compiled in 64-bit mode (on a 64-bit CPU), it will be the same size as *int64*. Below code is compiled in 32-bit mode: #+begin_src nim :flags --cpu:i386 --passC:-m32 --passL:-m32 import strformat echo fmt"Size of int32 / int / int64 = {sizeof(int32)} / *{sizeof(int)}* / {sizeof(int64)}" #+end_src #+RESULTS: : Size of int32 / int / int64 = 4 / *4* / 8 And below is compiled in the usual 64-bit mode -- Notice the change in the size of *int* type: #+begin_src nim import strformat echo fmt"Size of int32 / int / int64 = {sizeof(int32)} / *{sizeof(int)}* / {sizeof(int64)}" #+end_src #+RESULTS: : Size of int32 / int / int64 = 4 / *8* / 8 Below is also compiled using the default 64-bit mode. #+begin_src nim import strformat, typetraits var aInt: int = 1 aInt8: int8 = 2 aInt16: int16 = 3 aInt32: int32 = 4 aInt64: int64 = 5 echo fmt"aInt64 = {aInt64}, aInt32 = {aInt32}, aInt16 = {aInt16}, aInt8 = {aInt8}, aInt = {aInt}" aInt64 = aInt8 # works aInt64 = aInt16 # works aInt64 = aInt32 # works aInt64 = aInt # works echo fmt"aInt64 = {aInt64}, aInt32 = {aInt32}, aInt16 = {aInt16}, aInt8 = {aInt8}, aInt = {aInt}" aInt = aInt8 # works aInt = aInt16 # works aInt = aInt32 # works # aInt = aInt64 # Error: type mismatch: got but expected 'int' echo fmt"aInt64 = {aInt64}, aInt32 = {aInt32}, aInt16 = {aInt16}, aInt8 = {aInt8}, aInt = {aInt}" aInt32 = aInt8 # works aInt32 = aInt16 # works # aInt32 = aInt # Error: type mismatch: got but expected 'int32' # aInt32 = aInt64 # Error: type mismatch: got but expected 'int32' echo fmt"aInt64 = {aInt64}, aInt32 = {aInt32}, aInt16 = {aInt16}, aInt8 = {aInt8}, aInt = {aInt}" aInt16 = aInt8 # works # aInt16 = aInt32 # Error: type mismatch: got but expected 'int16' # aInt16 = aInt # Error: type mismatch: got but expected 'int16' # aInt16 = aInt64 # Error: type mismatch: got but expected 'int16' echo fmt"aInt64 = {aInt64}, aInt32 = {aInt32}, aInt16 = {aInt16}, aInt8 = {aInt8}, aInt = {aInt}" # aInt8 = aInt16 # Error: type mismatch: got but expected 'int8' # aInt8 = aInt32 # Error: type mismatch: got but expected 'int8' # aInt8 = aInt # Error: type mismatch: got but expected 'int8' # aInt8 = aInt64 # Error: type mismatch: got but expected 'int8' #+end_src #+RESULTS: : aInt64 = 5, aInt32 = 4, aInt16 = 3, aInt8 = 2, aInt = 1 : aInt64 = 1, aInt32 = 4, aInt16 = 3, aInt8 = 2, aInt = 1 : aInt64 = 1, aInt32 = 4, aInt16 = 3, aInt8 = 2, aInt = 4 : aInt64 = 1, aInt32 = 3, aInt16 = 3, aInt8 = 2, aInt = 4 : aInt64 = 1, aInt32 = 3, aInt16 = 2, aInt8 = 2, aInt = 4 For 64-bit compilation, while both *int* and *int64* types are 64-bit integer types, they are of *different "levels"*. /"Levels" is my home-grown term as I don't know how to explain this better based on what I am seeing./ Here's my attempt at explaining that: #+begin_verse > int64 > int > int32 > int16 > int8 #+end_verse My home-grown theory follows: - *int64* is at the "highest level", and *int8* is at the "lowest level". - You can assign a "lower level" typed variable to a "higher level" typed variable, but not the other way around. So that's why ~aInt64 = aInt~ works, but ~aInt = aInt64~ fails. Similarly ~aInt = aInt32~ works, but ~aInt32 = aInt~ fails --- That exact same would apply to the 32-bit compilation too. Related: - Section [[#primitive-types]] - Section [[#rand-int64]] ** Char and ~repr~ When printing /char/ values for debug, it's better to print using ~repr~ so that unprintable chars like ACK (~\6~) and BELL (~\7~) can be easily distinguished. See below for example: #+begin_src nim for c in @['\6', '\7', '\32', '\33', '\65', 'B']: echo "char with ascii value of ", c.ord, " = ", repr(c) #+end_src #+RESULTS: : char with ascii value of 6 = '\6' : char with ascii value of 7 = '\7' : char with ascii value of 32 = ' ' : char with ascii value of 33 = '!' : char with ascii value of 65 = 'A' : char with ascii value of 66 = 'B' ** Getting min and max for various data types :PROPERTIES: :CUSTOM_ID: data-types-min-max :END: *** Using ~low~ and ~high~ #+begin_src nim import math, strformat let int_low = low(int) int_low_2power = log2(-int_low.float).int int32_low = low(int32) int32_low_2power = log2(-int32_low.float).int int64_low = low(int64) int64_low_2power = log2(-int64_low.float).int echo fmt"int: {low(int)} (-2^{int_low_2power}) -> {high(int)} (2^{int_low_2power} - 1)" echo fmt"int32: {low(int32)} (-2^{int32_low_2power}) -> {high(int32)} (2^{int32_low_2power} - 1)" echo fmt"int64: {low(int64)} (-2^{int64_low_2power}) -> {high(int64)} (2^{int64_low_2power} - 1)" echo fmt"float: {low(float)} -> {high(float)}" echo fmt"float32: {low(float32)} -> {high(float32)}" echo fmt"float64: {low(float64)} -> {high(float64)}" #+end_src #+RESULTS: : int: -9223372036854775808 (-2^63) -> 9223372036854775807 (2^63 - 1) : int32: -2147483648 (-2^31) -> 2147483647 (2^31 - 1) : int64: -9223372036854775808 (-2^63) -> 9223372036854775807 (2^63 - 1) : float: -inf -> inf : float32: -inf -> inf : float64: -inf -> inf *** Using ~sizeof~ From [[https://devdocs.io/nim/system#sizeof,T][~sizeof~ docs]]: #+begin_quote returns the size of x in bytes. Since this is a low-level proc, *its usage is discouraged* - using ~new~ for the most cases suffices that one never needs to know x's size. As a special semantic rule, x may also be a type identifier (~sizeof(int)~ is valid). #+end_quote #+begin_src nim import strformat echo fmt"Size of bool is {sizeof(bool)} byte" echo fmt"Size of char is {sizeof(char)} byte" echo fmt"Size of int is {sizeof(int)} bytes" echo fmt"Size of int32 is {sizeof(int32)} bytes" echo fmt"Size of int64 is {sizeof(int64)} bytes" echo fmt"Size of float is {sizeof(float)} bytes" echo fmt"Size of float32 is {sizeof(float32)} bytes" echo fmt"Size of float64 is {sizeof(float64)} bytes" #+end_src #+RESULTS: : Size of bool is 1 byte : Size of char is 1 byte : Size of int is 8 bytes : Size of int32 is 4 bytes : Size of int64 is 8 bytes : Size of float is 8 bytes : Size of float32 is 4 bytes : Size of float64 is 8 bytes ** Boolean checking "unset" variables *** ~isNil~ non-string primitive types ~isNil~ does not work for non-string primitive types like ~bool~, ~char~, ~int~ and ~float~. #+begin_src nim :eval no import strformat, typetraits var b: bool c: char i: int f: float echo fmt"initial value of {b.type.name} b = {b}, isNil? {b.isNil}" echo fmt"initial value of {c.type.name} c = {c}, isNil? {c.isNil}" echo fmt"initial value of {i.type.name} i = {i}, isNil? {i.isNil}" echo fmt"initial value of {f.type.name} f = {f}, isNil? {f.isNil}" #+end_src Trying to evaluate the above gives this error (and then the similar for those other types in that code snippet too): #+begin_example lib/pure/strformat.nim(315, 39) Error: type mismatch: got but expected one of: proc isNil[T](x: seq[T]): bool proc isNil(x: cstring): bool proc isNil(x: string): bool proc isNil[T](x: ptr T): bool proc isNil[T](x: ref T): bool proc isNil(x: pointer): bool proc isNil[T: proc](x: T): bool expression: isNil(b) /bin/sh: /tmp/babel-2oW0wY/nim_src_FoNExx: Permission denied #+end_example *** ~isNil~ on string ~isNil~ does not work on strings too (after the removal of /nil/ as a valid string value in Nim 0.19.0+). #+begin_src nim :eval no var s: string echo s.isNil() #+end_src Trying to do so will give this error: #+begin_example nim_src_23lLuf.nim(6, 8) Error: usage of 'isNil' is a user-defined error #+end_example *** ~isNil~ on File object Thanks to {{{nuser(mashingan)}}} for [[https://forum.nim-lang.org/t/3863#24028][this tip]]. #+begin_src nim var fo: File echo "Before open: Is fo 'unset'? ", fo.isNil fo = open("./nim.org") echo "After open: Is fo 'unset'? ", fo.isNil fo.close echo "After close: Is fo 'unset'? ", fo.isNil fo = nil echo "After explicitly setting to nil: Is fo 'unset'? ", fo.isNil #+end_src #+RESULTS: : Before open: Is fo 'unset'? true : After open: Is fo 'unset'? false : After close: Is fo 'unset'? false : After explicitly setting to nil: Is fo 'unset'? true * String Stuff :PROPERTIES: :CUSTOM_ID: string-stuff :END: See [[#strings]] for an introduction to the string datatype in Nim. ** String Functions *** ~%~ and ~format~ The ~strutils~ module defines ~%~ and ~format~ for string formatting (along with many other things!). - https://nim-lang.org/docs/strutils.html ~%~ needs the second argument to be a string, or an array or sequence of strings. #+begin_src nim import strutils echo "$1 $2" % ["a", "b"] echo "$1 $2" % @["a", "b"] # echo "$1 $2" % [100, 200] # This gives error. % cannot have int list as arg, has to be an array/seq of strings # echo "$1 $2" % ['a', 'b'] # This gives error. % cannot have char list as arg, has to be an array/seq of strings echo "$1 $2" % [$100, $200] #+end_src #+results: : a b : a b : 100 200 ~format~ does not have the requirement for the input to be a string (or an array/seq of strings) -- It auto-stringifies the elements in the second argument. #+begin_src nim import strutils echo "$1 $2".format(["a", "b"]) echo "$1 $2".format("a", "b") echo "$1 $2".format('a', 'b') # format, unlike % does auto-stringification of the input echo "$1 $2".format(100, 200) # format, unlike % does auto-stringification of the input #+end_src #+RESULTS: : a b : a b : a b : 100 200 *** ~strformat~ and ~fmt~ :PROPERTIES: :CUSTOM_ID: strformat :END: This is a standard Nim library. - https://nim-lang.org/docs/strformat.html Refer to this separate set of [[https://scripter.co/notes/nim-fmt/][notes]] that explains ~fmt~ (or ~&~) from the ~strformat~ library in great detail, including examples for all options in the ~fmt~ format specifier. #+begin_src nim import strformat let a = 100 b = "abc" echo fmt"a = {a}, b = {b}" #+end_src #+RESULTS: : a = 100, b = abc **** TO__BE__FIXED Use string variable containing message formatting for ~fmt~ CLOSED: [2018-06-28 Thu 15:11] Below does not work: #+begin_src nim :eval no import strformat let a = 100 msg = "a = {a}" echo fmt(msg) #+end_src Gives error: #+begin_example stack trace: (most recent call last) lib/pure/strformat.nim(281) & nim_src_ylMbyJ.nim(9, 10) Error: string formatting (fmt(), &) only works with string literals #+end_example I understand that this is a limitation of the nature of implementation of ~fmt~ because it needs its formatting string available at compile time. But I wish this limitation wasn't there. *** String functions: Nim vs Python Refer to this separate set of [[https://scripter.co/notes/string-fns-nim-vs-python/][notes]] where I compare the Nim String functions with those in Python 3. ** String and character literals - String literals are enclosed in double quotes - Character literals in single quotes. - The single quote character literal is represented by ~'\39'~. Special characters are escaped with =\=: =\n= means newline, =\t= means tabulator, etc. #+begin_src nim echo "Hello" # echo 'Hello' # This will give error; single quotes are only for single character echo 'a', 'b', '\39' echo "c\n", '\t', 'b' #+end_src #+results: : Hello : ab' : c : b There are also raw string literals: #+begin_src nim echo r"No need to escape \n and \t in here" #+end_src #+results: : No need to escape \n and \t in here In raw literals the backslash is not an escape character. The third and last way to write string literals are long string literals like in Python. They are written with three quotes: =""" ... """=; they can span over multiple lines and the =\= is not an escape character either. #+begin_src nim echo """Hey look at this. I can keep on typing over multiple lines, with 'single' or "double" quotes and even back slashes: \n \t \r""" #+end_src #+results: : Hey look at this. : I can keep on typing over multiple lines, : with 'single' or "double" quotes and even : back slashes: \n \t \r ** String concatenation Use ~&~. #+begin_src nim let s = "abc" & "def" echo s echo "ghi" & "jkl" #+end_src #+RESULTS: : abcdef : ghijkl ** String Comparison ~==~ and ~!=~ can be used to compare if two strings are equal. #+begin_src nim assert "abc" == "abc" assert "abc" != "acb" #+end_src #+RESULTS: The above is obvious. But see below for the "less than (or equal to)" and "greater than (or equal to)" comparisons: - If strings /X/ and /Y/ are of equal lengths, walking through the characters of both strings from the left-most side, for the first set of non-equal characters /char-in-X/ and /char-in-Y/, /char-in-X/ < /char-in-Y/ => /X/ < /Y/. #+begin_src nim assert "a" < "b" assert "bb" > "ba" assert "ab" < "ba" assert "abc" < "abd" assert "bbc" > "abc" #+end_src #+RESULTS: - The above rule applies even if strings /X/ and /Y/ are *not* of equal lengths. So even if string /X/ has more characters than string /Y/, /X/ would be /less than/ /Y/ if for the first set of non-equal characters /char-in-X/ and /char-in-Y/, /char-in-X/ < /char-in-Y/. #+begin_src nim assert "a" < "ab" assert "ab" > "a" assert "ab" < "b" #+end_src #+RESULTS: So string comparison is *not* a good way for version comparison: #+begin_src nim echo NimVersion assert NimVersion >= "0.19.0" assert NimVersion < "00.19.0" # Surprise! assert NimVersion >= "0.09.0" assert NimVersion < "0.9.0" # Surprise! #+end_src #+RESULTS: : 0.19.0 See [[*Tuple comparison]] instead for Nim version comparison. * Unicode ** Rune Just as plain ASCII strings are composed of *chars*, strings with Unicode characters (>8-bits) are composed of *Runes*. - The functions from ~strutils~ like ~isAlphaNumeric~, ~isAlphaAscii~ work on /chars/ and /strings/. - The functions from ~unicode~ like ~isAlpha~, ~isLower~, ~isUpper~, etc. work on /Runes/ and /strings/. #+begin_src nim import unicode from strutils import isAlphaNumeric, isAlphaAscii echo 'a' echo 'a'.isAlphaAscii() echo 'a'.isAlphaNumeric() # echo 'a'.isAlpha() # this gives error: nim_src_YQy3FE.nim(6, 9) Error: type mismatch: got echo 'a'.Rune echo 'a'.Rune.isLower() echo 'a'.Rune.isUpper() echo 'a'.Rune.isAlpha() #+end_src #+RESULTS: : a : true : true : a : true : false : true ** Walking through chars vs Runes :PROPERTIES: :CUSTOM_ID: char-rune-walk :END: Unicode symbols are allowed in strings, but are not treated in any special way, so if you want count glyphs or uppercase Unicode symbols, you must use the ~unicode~ module. #+name: code__char_walk #+caption: Walking through *chars* in a string #+begin_src nim import strformat let str = "કૌશલ" echo "Here is how it looks when str is parsed char-by-char:" echo fmt" str = {str}, number of chars = {str.len}" for i, c in str: echo fmt" char {i} = {repr(c)} ({ord(c):#x})" #+end_src #+RESULTS: code__char_walk #+begin_example Here is how it looks when str is parsed char-by-char: str = કૌશલ, number of chars = 12 char 0 = '\224' (0xe0) char 1 = '\170' (0xaa) char 2 = '\149' (0x95) char 3 = '\224' (0xe0) char 4 = '\171' (0xab) char 5 = '\140' (0x8c) char 6 = '\224' (0xe0) char 7 = '\170' (0xaa) char 8 = '\182' (0xb6) char 9 = '\224' (0xe0) char 10 = '\170' (0xaa) char 11 = '\178' (0xb2) #+end_example #+name: code__rune_walk #+caption: Walking through *Runes* in a string #+begin_src nim import unicode, strformat let str = "કૌશલ" echo "Here is how it looks when str is parsed rune-by-rune:" echo fmt" str = {str}, number of Runes = {str.runeLen}" for i, r in str.toRunes: echo fmt" Rune {i} = {r}" #+end_src #+RESULTS: code__rune_walk : Here is how it looks when str is parsed rune-by-rune: : str = કૌશલ, number of Runes = 4 : Rune 0 = ક : Rune 1 = ૌ : Rune 2 = શ : Rune 3 = લ Strings are generally considered to be encoded as UTF-8. So because of Unicode's backwards compatibility, they can be treated exactly as ASCII, with all values above 127 ignored. ** Unidecode [[https://nim-lang.org/docs/unidecode.html][~unidecode~]] is a Nim stdlib. It is used to transliterate unicode chars (non-English languages, etc.) to English ASCII characters -- [[https://forum.nim-lang.org/t/4154][Ref]]. #+begin_note When importing ~unidecode~, most likely you would want to compile your Nim code with the ~-d:embedUnidecodeTable~ switch. #+end_note #+name: code__unidecode_with_embedUnidecodeTable #+caption: Example of transliteration using ~unidecode~ (compiled *with* ~-d:embedUnidecodeTable~) #+begin_src nim :noweb yes :flags -d:embedUnidecodeTable import unidecode echo unidecode("મારુ નામ કૌશલ મોદી છે. હૂં અમદાવાદ થી છું.") echo unidecode("Æneid") echo unidecode("北京") # Below is the same as above echo unidecode("\xe5\x8c\x97\xe4\xba\xac") #+end_src #+RESULTS: code__unidecode_with_embedUnidecodeTable : maaru naam kaushl modii che. huuN amdaavaad thii chuN. : AEneid : Bei Jing : Bei Jing #+begin_note In Emacs, with point over 北, if I do ~C-u C-x =~, I get: #+begin_quote buffer code: #xE5 #x8C #x97 #+end_quote So I translated that to ~\xe5\x8c\x97~, and then the same for 京. #+end_note If the ~embedUnidecodeTable~ symbol is not defined during compilation, an explicit call of the ~loadUnidecodeTable~ proc is needed, with the path to the ~unidecode.dat~ file as its argument. Below code is compiled without the ~-d:embedUnidecodeTable~ switch. - So the [[* Nim StdLib path][~findNimStdLib~ proc]] is first defined to find the path to the installed Nim standard libraries, and based on that, the path to the inbuilt ~unidecode.dat~ is derived. - Then ~loadUnidecodeTable~ proc is used to load that inbuilt table. #+name: code__unidecode_without_embedUnidecodeTable #+caption: Example of transliteration using ~unidecode~ (compiled without ~-d:embedUnidecodeTable~) #+begin_src nim :noweb yes import unidecode <> # Load the Unicode data file. loadUnidecodeTable(findNimStdLib() / "pure/unidecode/unidecode.dat") echo unidecode("મારુ નામ કૌશલ મોદી છે. હૂં અમદાવાદ થી છું.") echo unidecode("Æneid") echo unidecode("北京") # Below is the same as above echo unidecode("\xe5\x8c\x97\xe4\xba\xac") #+end_src #+RESULTS: code__unidecode_without_embedUnidecodeTable : maaru naam kaushl modii che. huuN amdaavaad thii chuN. : AEneid : Bei Jing : Bei Jing #+begin_note See [[* Nim StdLib path]] for the source of ~findNimStdLib~. #+end_note * Math ** Log *** ~log~ Credit for the below ~log~ function goes to {{{guser(Paalon)}}} [[[https://github.com/nim-lang/Nim/pull/7950][ref]]]: #+begin_src nim :noweb-ref math_log :eval no :exports none import math proc log [X, B: SomeFloat](x: X, base: B = E): auto = ## Computes the logarithm ``base`` of ``x``. ## If ``base`` is not specified, it defaults to the natural base ``E``. when B is float64 or X is float64: var r: float64 else: var r: float32 if base == E: r = ln(x) else: r = ln(x) / ln(base) return r #+end_src #+begin_src nim :noweb yes <> import random, strformat echo fmt"log10(111) = {log(111.float, 10.float)}" echo fmt"log2(8) = {log(8.float, 2.float)}" echo fmt"ln(8) = {log(8.float, E)}" echo fmt"ln(8) = {log(8.float)}" #+end_src #+RESULTS: : log10(111) = 2.045322978786657 : log2(8) = 3.0 : ln(8) = 2.079441541679836 : ln(8) = 2.079441541679836 **** Test the ~log~ proc :noexport: #+begin_src nim :noweb yes <> import random, strformat for _ in 0 .. 9: var x = rand(1_000).float / rand(1_000).float l1 = log(x) l2 = ln(x) / ln(E) echo fmt"ln({x}) = {l1}, {l2}" doAssert l1 == l2 #+end_src #+RESULTS: #+begin_example ln(3.4) = 1.223775431622116, 1.223775431622116 ln(0.2420494699646643) = -1.418613152375058, -1.418613152375058 ln(2.481366459627329) = 0.9088094002218663, 0.9088094002218663 ln(1.588235294117647) = 0.462623521948113, 0.462623521948113 ln(11.42857142857143) = 2.436116485618568, 2.436116485618568 ln(0.4278846153846154) = -0.8489017099691781, -0.8489017099691781 ln(0.04148471615720524) = -3.182430204947745, -3.182430204947745 ln(0.5716560509554141) = -0.5592177779799198, -0.5592177779799198 ln(4.774193548387097) = 1.563225069278969, 1.563225069278969 ln(0.7587131367292225) = -0.276131522000578, -0.276131522000578 #+end_example *** ~log2~ #+begin_src nim import math, strformat var val = 8 power2 = log2(val.float) # need to cast val to a float echo fmt"2^{power2} = {val}" #+end_src #+RESULTS: : 2^3.0 = 8 *** ~log10~ #+begin_src nim import math, strformat var val = 100 power10 = log10(val.float) # need to cast val to a float echo fmt"10^{power10} = {val}" #+end_src #+RESULTS: : 10^2.0 = 100 ** Exponentiation *** Using ~^~ from ~math~ module (Non-negative integer exponentiation) :PROPERTIES: :CUSTOM_ID: math-circumflex :END: Need to import ~math~ module for the exponentiation operator ~^~ to work. You can do ~X ^ Y~ where ~X~ can be an integer or float, but *~Y~ has to be an integer >= 0*. #+begin_src nim import math echo (2^0) echo (2^3) echo (2.2^3) #+end_src #+RESULTS: : 1 : 8 : 10.648 *** Using ~pow~ from ~math~ module (Float exponentiation) :PROPERTIES: :CUSTOM_ID: math-pow :END: As mentioned above, ~X ^ Y~ works only if ~Y~ is an integer *and* >= 0. But what if ~Y~ is negative, or not an integer? .. In that case, you need to use the ~pow~ function from the same ~math~ module. #+begin_src nim import math echo "Evaluating equivalent of 2^-1:" echo pow(2.0, -1.0) #+end_src #+RESULTS: : Evaluating equivalent of 2^-1: : 0.5 **** About ~pow~ and floats Note that using ~pow~ has a requirement -- Both of its arguments need to be *floats*. So the below snippet will fail: #+begin_src nim :eval no import math echo pow(2, 1) #+end_src #+begin_example nim_src_We7gQw.nim(5, 9) Error: ambiguous call; both math.pow(x: float64, y: float64)[declared in lib/pure/math.nim(265, 7)] and math.pow(x: float32, y: float32)[declared in lib/pure/math.nim(264, 7)] match for: (int literal(2), int literal(1)) #+end_example But below will work: #+begin_src nim import math echo pow(2.0, 1.0) echo pow(2.float, 1.float) # same as above #+end_src #+RESULTS: : 2.0 : 2.0 ** Random *** Random ~bool~ #+begin_src nim import random proc randBool(): bool = result = rand(1).bool for _ in 0 .. 5: echo randBool() #+end_src #+RESULTS: : true : false : true : true : false : false *** Random range :PROPERTIES: :CUSTOM_ID: random-range :END: Use ~rand(LOWER .. UPPER)~. #+begin_src nim import random for _ in 0 .. 5: echo rand(4 .. 5) #+end_src #+RESULTS: : 5 : 4 : 5 : 5 : 4 : 4 Works with negatives too: #+begin_src nim import random for _ in 0 .. 5: echo rand(-5 .. 5) #+end_src #+RESULTS: : -4 : 5 : 0 : 0 : 2 : -2 *** Call ~randomize~ before ~rand~ If you call just the ~rand~ function without first calling ~randomize~, you will get the very same random value each time. #+caption: ~rand~ without ~randomize~ #+name: code__rand_without_randomize #+begin_src nim import random echo rand(high(int)) #+end_src #+RESULTS: code__rand_without_randomize : 4292486321577947087 In order to get /truly/ random output, first call ~randomize~ and then ~rand~. If you evaluate the below snippet, you will very likely get a different output each time, unlike the above snippet. #+caption: ~rand~ with ~randomize~, no seed #+name: code__rand_with_randomize_no_seed #+begin_src nim import random randomize() echo rand(high(int)) #+end_src #+RESULTS: code__rand_with_randomize_no_seed : 3156573264287169223 **** Specifying randomization seed You can also have a use-case where you want to specify a particular randomization seed. In that case, pass that /integer/ seed to the ~randomize~ function. Re-evaluating below will result in the same output each time, like in [[code__rand_without_randomize]], but with the difference that you can control the randomization seed. #+caption: ~rand~ with ~randomize~, *with* seed #+name: code__rand_with_randomize_with_seed #+begin_src nim import random randomize(123) echo rand(high(int)) #+end_src #+RESULTS: code__rand_with_randomize_with_seed : 8452497653883 **** ~randomize(0)~ disables randomization From tests, it looks like ~randomize(0)~ disables randomization altogether and hard-codes the "randomized" var to 0! /Whether this is intended or not, this behavior is quite odd./ #+begin_src nim import random echo "Setting randomization seed to a non-zero value; value of 1 is picked arbitrarily:" randomize(1) for _ in 0 .. 4: echo rand(high(int)) echo "\nNow setting randomization seed to 0:" randomize(0) for _ in 0 .. 4: echo rand(high(int)) #+end_src #+RESULTS: #+begin_example Setting randomization seed to a non-zero value; value of 1 is picked arbitrarily: 68719493121 38280734540038433 1153018330890649890 1842080154353508865 97957842178638929 Now setting randomization seed to 0: 0 0 0 0 0 #+end_example *** ~rand~ range #+begin_src nim import random let k = 3 randomize(2) # arbitrarily picked seed for _ in 0 .. (2*k): echo rand(k) #+end_src #+RESULTS: : 2 : 2 : 0 : 3 : 2 : 0 : 1 As seen above, ~rand(k)~ returns a random value in the range ~[0,k]~ i.e. including the "k" value. *** Limit of ~rand~ :PROPERTIES: :CUSTOM_ID: rand-limit :END: ~rand~ accepts ~int~ (I think that is same is ~int64~ -- because [[#data-types-min-max][this]]) and ~float~ (probably same as ~float32~? or ~float64~?.. couldn't tell from [[#data-types-min-max][that]]). From [[https://devdocs.io/nim/random#rand,Rand,int][~random.rand~ docs]]: #+begin_quote Returns a random number in the range 0 .. max. #+end_quote As ~rand~ accepts ~int~, and it's size is same as ~int64~, and is *signed* (use ~uint~ for unsigned), the max positive number that ~rand~ can generate is ~2^63 - 1~. **** DONE ~rand~ does not accept ~int64~ input CLOSED: [2018-06-20 Wed 14:15] :PROPERTIES: :CUSTOM_ID: rand-int64 :END: Below gives an error. #+begin_src nim :eval no import random, strformat echo fmt"rand(9223372036854775807) = {rand(9223372036854775807)}" #+end_src #+begin_example nim_src_yz0syY.nim(5, 10) Error: type mismatch: got but expected one of: proc rand[T](r: var Rand; x: HSlice[T, T]): T proc rand(max: float): float proc rand(max: int): int proc rand(r: var Rand; max: float): float proc rand[T](r: var Rand; a: openArray[T]): T proc rand[T](a: openArray[T]): T proc rand[T](x: HSlice[T, T]): T proc rand(r: var Rand; max: int): int expression: rand(9223372036854775807'i64) #+end_example That's because the ~rand~ proc doesn't accept/return ~int64~ type value. But it does accept ~int~ type. Even though both ~int~ and ~int64~ might be 64-bit integer types, they are not technically the same type! #+begin_src nim import random, strformat echo fmt"rand(9223372036854775807.int) = {rand(9223372036854775807.int)}" #+end_src #+RESULTS: : rand(9223372036854775807.int) = 4292486321577947087 - [[https://forum.nim-lang.org/t/3851][/int/ vs /int64/ on Nim Forums]] ** Quotient and Remainder If you do "7 / 2", the /quotient/ is 3, and the /remainder/ is 1. *** Quotient The quotient is calculated using the binary *operator* [[https://nim-lang.org/docs/system.html#div,T,T][~div~]] which basically does integer division. - The result is negative if either of the /dividend/ or the /divisor/ is negative; else it is positive. #+begin_src nim import strformat echo "divisor = 3" for i in -4 .. 4: echo fmt" {i:2} div 3 = {i div 3:2}" echo "\ndivisor = -3" for i in -4 .. 4: echo fmt" {i:2} div -3 = {i div -3:2}" #+end_src #+RESULTS: #+begin_example divisor = 3 -4 div 3 = -1 -3 div 3 = -1 -2 div 3 = 0 -1 div 3 = 0 0 div 3 = 0 1 div 3 = 0 2 div 3 = 0 3 div 3 = 1 4 div 3 = 1 divisor = -3 -4 div -3 = 1 -3 div -3 = 1 -2 div -3 = 0 -1 div -3 = 0 0 div -3 = 0 1 div -3 = 0 2 div -3 = 0 3 div -3 = -1 4 div -3 = -1 #+end_example *** Remainder / Modulo operator The remainder is calculated using the binary *operator*, [[https://en.wikipedia.org/wiki/Modulo_operation][Modulo operator]], [[https://nim-lang.org/docs/system.html#mod,T,T][~mod~]]. - The result has the sign of the /dividend/ i.e. the sign of the /divisor/ is ignored. #+begin_src nim import strformat echo "divisor = 3" for i in -4 .. 4: echo fmt" {i:2} mod 3 = {i mod 3:2}" echo "\ndivisor = -3" for i in -4 .. 4: echo fmt" {i:2} mod -3 = {i mod -3:2}" #+end_src #+RESULTS: #+begin_example divisor = 3 -4 mod 3 = -1 -3 mod 3 = 0 -2 mod 3 = -2 -1 mod 3 = -1 0 mod 3 = 0 1 mod 3 = 1 2 mod 3 = 2 3 mod 3 = 0 4 mod 3 = 1 divisor = -3 -4 mod -3 = -1 -3 mod -3 = 0 -2 mod -3 = -2 -1 mod -3 = -1 0 mod -3 = 0 1 mod -3 = 1 2 mod -3 = 2 3 mod -3 = 0 4 mod -3 = 1 #+end_example ** Incrementing/Decrementing *** Incrementing ~inc~ proc increments variables of [[https://nim-lang.org/docs/manual.html#types-ordinal-types][/Ordinal types/]]: ~int~, ~char~, ~enum~. ~inc a~ is similar to doing ~a = a + 1~ or ~a += 1~ for ~int~ or ~char~ type variable ~a~. #+begin_src nim var a = 100 echo a inc a echo a inc a echo a #+end_src #+RESULTS: : 100 : 101 : 102 #+begin_src nim var a = 'b' echo a inc a echo a inc a echo a #+end_src #+RESULTS: : b : c : d #+begin_src nim type Direction = enum north, east, south, west var a = north echo a inc a echo a inc a echo a #+end_src #+RESULTS: : north : east : south *** Decrementing ~dec~ proc decrements variables of [[https://nim-lang.org/docs/manual.html#types-ordinal-types][/Ordinal types/]]: ~int~, ~char~, ~enum~. ~dec a~ is similar to doing ~a = a - 1~ or ~a -= 1~ for ~int~ or ~char~ type variable ~a~. #+begin_src nim var a = 100 echo a dec a echo a dec a echo a #+end_src #+RESULTS: : 100 : 99 : 98 #+begin_src nim var a = 'b' echo a dec a echo a dec a echo a #+end_src #+RESULTS: : b : a : ` #+begin_src nim type Direction = enum north, east, south, west var a = west echo a dec a echo a dec a echo a #+end_src #+RESULTS: : west : south : east * Variable "Types" #+attr_html: :class wide-table #+name: tab__var_vs_let_vs_const #+caption: ~var~ vs ~let~ vs ~const~ | | | <25> | <20> | <20> | | Keyword | Variable type | Must be initialized? | Can be set during runtime? | Should be /evallable/ at compile? | |---------+---------------+-------------------------------------------------------+----------------------------+-----------------------------------| | =var= | Mutable | No (type must be specified though if not initialized) | Yes, multiple times | No | | =let= | Immutable | Yes, though these can be initialized at run time. | Yes, just *once* | No | | =const= | Constant | Yes | No | Yes | #+TBLFM: ** Mutable variables (~var~) ~var~ variables are mutable. #+begin_src nim var a = "foo" b = 0 c: int # Works fine, initialized to 0 # Works fine, `a` is mutable a.add("bar") echo a b += 1 echo b c = 3 echo c c = 7 echo c #+end_src #+results: : foobar : 1 : 3 : 7 ** Immutable variables (~let~) ~let~ variables are not mutable, but they can be set at run time. #+begin_src nim let d = "foo" e = 5 # Compile-time error, must be initialized at creation # f: float # Below line fixes the error f: float = 2.2 # Compile-time error, `d` and `e` are immutable # Below 2 lines are commented out to fix the compilation error # d.add("bar") # e += 1 echo d echo e echo f #+end_src #+results: : foo : 5 : 2.2 - [[https://nim-lang.org/docs/tut1.html#the-let-statement][Nim Tutorial -- =let= statement]] ** Constants (~const~) ~const~ "variables" are not mutable, and they have to be set at compile time. #+begin_src nim # Computed at compilation time const s = "abcdef" sLen = s.len echo s echo sLen #+end_src #+RESULTS: : abcdef : 6 *** ~let~ vs ~const~ The difference between =let= and =const= is: - =let= allows a variable value to be assigned at run time (though it cannot be re-assigned). #+begin_src nim :eval no let input = readLine(stdin) # works #+end_src - =const= means "enforce compile time evaluation and put it into a data section" i.e. you should be able to evaluate the value of a =const= variable during compile time. So setting a =const= variable using =readLine= (that takes user input at run time) will result in an error. #+begin_src nim :eval no const input = readLine(stdin) # Error: constant expression expected #+end_src ** =return= keyword and =result= Variable :PROPERTIES: :CUSTOM_ID: return-and-result :END: Below example shows the use of ~return~ keyword: #+caption: =getAlphabet= function implementation *without* using =result= variable #+name: code__getAlphabet_v1 #+begin_src nim proc getAlphabet(): string = var accm = "" for letter in 'a' .. 'z': # see iterators accm.add(letter) return accm echo getAlphabet() #+end_src #+results: code__getAlphabet_v1 : abcdefghijklmnopqrstuvwxyz The =result= variable is a special variable that serves as an implicit /return/ variable, which exists because the control flow semantics of the =return= statement are rarely needed. The =result= variable is initialized in the standard way, as if it was declared with: #+begin_src nim :eval no var result: ReturnType #+end_src For example, the =getAlphabet()= function above could be rewritten more concisely as: #+caption: =getAlphabet= function implementation using =result= variable #+name: code__getAlphabet_v2 #+begin_src nim proc getAlphabet(): string = result = "" for letter in 'a' .. 'z': result.add(letter) echo getAlphabet() #+end_src #+results: code__getAlphabet_v2 : abcdefghijklmnopqrstuvwxyz A possible gotcha is declaring a new variable called =result= and expecting it to have the same semantics. #+begin_src nim proc unexpected(): int = var result = 5 # Uh-oh, here 'result' got declared as a local variable because of 'var' keyword result += 5 echo unexpected() # Prints 0, not 10 #+end_src #+results: : 0 ** Variable Auto-initialization *Global* variables (like the ones in below example) cannot remain *uninitialized* in Nim.. they are always auto-initialized to some value. #+name: code__auto_init_global_vars #+caption: Global variables always get auto-initialized #+begin_src nim var fooBool: bool fooInt: int fooFloat: float fooString: string fooSeqString: seq[string] echo "Value of 'uninitialized' variable 'fooBool': ", fooBool echo "Value of 'uninitialized' variable 'fooInt': ", fooInt echo "Value of 'uninitialized' variable 'fooFloat': ", fooFloat echo "Value of 'uninitialized' variable 'fooString': ", fooString echo "Value of 'uninitialized' variable 'fooSeqString': ", fooSeqString #+end_src #+RESULTS: code__auto_init_global_vars : Value of 'uninitialized' variable 'fooBool': false : Value of 'uninitialized' variable 'fooInt': 0 : Value of 'uninitialized' variable 'fooFloat': 0.0 : Value of 'uninitialized' variable 'fooString': : Value of 'uninitialized' variable 'fooSeqString': @[] ** Uninitialized variables From section [[* Variable Auto-initialization]], we see that global variables always get auto-initialized. Thanks to {{{guser(data-man)}}}, I learn that *local* variables can remain uninitialized, with the use of the ~noinit~[fn:2] pragma. *Local* variables would be the ones like the ~var~ variables inside a ~proc~. As global variables always auto-initialize, the ~{.noinit.}~ pragma will not work in the below snippet. The ~ui_i~ var still auto-initializes with all elements set to 0. #+begin_note Nim should have thrown an error if someone tried to use ~{.noinit.}~ for global vars, but instead it silently does nothing! #+end_note #+name: code__noinit_not_working_for_global_var #+caption: ~noinit~ pragma does not work for global variables #+begin_src nim var ui_i {.noinit.}: array[3, int] echo "Value of uninitialized variable 'ui_i': ", ui_i #+end_src #+RESULTS: code__noinit_not_working_for_global_var : Value of uninitialized variable 'ui_i': [0, 0, 0] *** Pollute Stack A little function ~PolluteStack~ to help test if [[* Uninitialized variables][~{.noinit.}~]] works. #+begin_src nim :eval no :noweb-ref polluteStack import random, math proc polluteStack() = var c: char = (rand(high(char).int)).char i32: int32 = (rand(high(int32).int)).int32 i64: int64 = (rand(high(int64).int)).int64 f32: float32 = (rand(high(int64).int)).float32 # Intentionally using high(int64); not a typo f64: float64 = (rand(high(int64).int)).float64 b: bool = rand(1).bool #+end_src I came up with the above function after reading this [[https://forum.nim-lang.org/t/3699#23059][priceless tip by /Stefan Salevski/]]: #+begin_quote You may try to call another proc which writes some values to local variables, that should pollute the stack. And then call your ff proc. #+end_quote *I am open to learn better ways to pollute the stack.* **** Break-down of the ~polluteStack~ function We will use just this one line for analysis, as the rest are similar. #+begin_src nim :eval no f32: float32 = (rand(high(int64).int)).float32 #+end_src - ~f32~ is of type ~float32~. So whatever we are assigning it to must be of that type. Here, that /whatever/ is ~(rand(high(int64).int))~, and we are casting it to ~float32~ type using ~(WHATEVER).float32~. - So here /whatever/ is ~rand~ which takes in ~high(int64).int~ as input. - The input to ~rand~ is casted to ~int~ using ~.int~ because as of writing this, it did not accept inputs of type ~int64~. - ~high(int64)~ returns the maximum value of ~int64~ type. - Both ~float32~ and ~float64~ have min value of ~-inf~ and max value of ~+inf~. The 32-bit/64-bit only changes the number of bytes using internally for storing those float values. So it is OK to use the same ~rand(high(int64).int~ to generate a random number for both of these float types as long as we are casting them using the right type for the right variable. For simplicity, this function generates only positive random values for each type. **** Test ~polluteStack~ #+begin_src nim :noweb yes <> echo repr(c) echo i32 echo i64 echo f32 echo f64 echo b randomize(123) # arbitarily picked seed polluteStack() #+end_src #+RESULTS: : '{' : 805341723 : 3469772516323406999 : 4.753479106664858e+17 : 7.875093343280577e+18 : true *** ~{.noinit.}~ does not work in ~block~ too I thought that variables in a ~block~ would be /local/ for the purpose of ~{.noinit.}~. But they are not i.e. the no-initialization does not work here too! #+begin_src nim :noweb yes <> # local var in block block foo: randomize(123) # arbitarily picked seed polluteStack() var ui_i {.noinit.}: array[3, int] echo "In block: Value of uninitialized variable 'ui_i' in block: ", ui_i #+end_src #+RESULTS: : In block: Value of uninitialized variable 'ui_i' in block: [0, 0, 0] *** Works for ~var~ variables in ~proc~ Finally, the below code returns random values as that *local* ~var~, local to ~a~ ~proc~ will actually remain /uninitialized/: #+name: code__noinit_working_for_local_var #+caption: ~noinit~ pragma works for local variables #+begin_src nim :noweb yes <> proc a() = var ui_i1 {.noinit.}: int ui_i2 {.noinit.}: array[3, int] echo "Value of uninitialized variable 'ui_i1': ", ui_i1 echo "Value of uninitialized variable 'ui_i2': ", ui_i2 randomize(123) # arbitarily picked seed polluteStack() a() #+end_src #+RESULTS: code__noinit_working_for_local_var : Value of uninitialized variable 'ui_i1': 123 : Value of uninitialized variable 'ui_i2': [3458916362389291184, 805341723, 8863224799858589696] - Earlier confusion :: Even for local vars, if the vars were not arrays, I was unable to have them echo with random values with the ~noinit~ pragma. {{{nimissue(7852)}}} has some interesting code snippets that behaved differently wrt ~noinit~ between me and /data-man/. - Fix :: <2018-05-23 Wed> Above issue got fixed once I started using the [[* Pollute Stack][~polluteStack~]] proc. **** Examples of uninitialized arrays Here are some more examples of uninitialized local variables of array types: #+begin_src nim :noweb yes <> proc a() = var ai {.noinit.}: array[3, int] ac {.noinit.}: array[3, char] ab {.noinit.}: array[3, bool] af {.noinit.}: array[3, float] echo ai echo ac echo ab echo af randomize(123) # arbitarily picked seed polluteStack() a() #+end_src #+RESULTS: : [3458916362389291184, 805341723, 8863224791268655104] : ['\e', '\'', '0'] : [true, true, true] : [1.421348378094312e+139, 6.988083611441626e-277, 9.978350516174709e-77] - Earlier confusion :: If I commented out the int array declaration in the above code (of course, its ~echo~ too), the ~char~ and ~bool~ arrays would start auto-initializing, but not the ~float~. - Fix :: <2018-05-23 Wed> Above issue got fixed once I started using the [[* Pollute Stack][~polluteStack~]] proc. The confusion was created because the stack was clean.. needed something to pollute the stack first. **** Scalar local vars and ~noinit~ - Earlier confusion :: Looks like ~noinit~ works only for the ~float64~ var. - Fix :: <2018-05-23 Wed> Above issue got fixed once I started using the [[* Pollute Stack][~polluteStack~]] proc. [[https://github.com/nim-lang/Nim/issues/7852#issuecomment-390552470][ref]] #+begin_src nim :noweb yes <> proc bar() = var i32: int32 i64: int64 f32: float32 f64: float64 i32ni {.noInit.}: int32 i64ni {.noInit.}: int64 f32ni {.noInit.}: float32 f64ni {.noInit.}: float64 echo "i32 (auto-init) = ", i32 echo "i64 (auto-init) = ", i64 echo "f32 (auto-init) = ", f32 echo "f64 (auto-init) = ", f64 echo "i32ni (no init) = ", i32ni echo "i64ni (no init) = ", i64ni echo "f32ni (no init) = ", f32ni echo "f64ni (no init) = ", f64ni randomize(123) # arbitarily picked seed polluteStack() bar() #+end_src #+RESULTS: : i32 (auto-init) = 0 : i64 (auto-init) = 0 : f32 (auto-init) = 0.0 : f64 (auto-init) = 0.0 : i32ni (no init) = 807869368 : i64ni (no init) = 3469772516323406999 : f32ni (no init) = 5.746757700654961e-35 : f64ni (no init) = 1.42134889750871e+139 * Types ** Check Type (~is~) :PROPERTIES: :CUSTOM_ID: check-type :END: Use the [[https://devdocs.io/nim/system#is,T,S][~is~]] proc like an operator. ~FOO is TYPE~ returns ~true~ if ~FOO~ is of type ~TYPE~. #+begin_src nim echo "'c' is char? ", 'c' is char echo "'c' is string? ", 'c' is string echo """"c" is string? """, "c" is string #+end_src #+RESULTS: : 'c' is char? true : 'c' is string? false : "c" is string? true ** Heterogeneous Slice (HSlice) :PROPERTIES: :CUSTOM_ID: hslice :END: - [[https://nim-lang.org/docs/system.html#HSlice][Nim Docs -- HSlice]] The heterogeneous slice type is an /object/ of 2 elements -- which can be of different types (and thus.. heterogeneous). #+begin_src nim :eval no HSlice[T; U] = object a*: T ## the lower bound (inclusive) b*: U ## the upper bound (inclusive) #+end_src An /HSlice/ can be created using below: #+begin_src nim import strformat, typetraits var s1: HSlice[int, char] s1.a = 4 s1.b = 'f' echo fmt"s1={s1} is of type {$type(s1)}" #+end_src #+RESULTS: : s1=(a: 4, b: 'f') is of type HSlice[system.int, system.char] But that verbose style of assignment doesn't look like a lot of fun. So the double-dot operator [[https://nim-lang.org/docs/system.html#..,T,U][~..~]] is defined to quickly create /HSlices/. Below snippet creates the exact same /HSlice/ as in the above snippet: #+begin_src nim import strformat, typetraits let s1 = 4 .. 'f' echo fmt"s1={s1} is of type {$type(s1)}" #+end_src #+RESULTS: : s1=(a: 4, b: 'f') is of type HSlice[system.int, system.char] The /HSlices/ are very commonly used with both elements having the same type (that too, usually ~int~ or ~char~) --- in [[#slice-iterators][slice iterators]], or specify a range of numbers to [[#random-range][randomize]] inbetween, or to get a slice of a string, or a sequence or array. Also see [[#slice][/Slice/]]. #+name: code__hslice_1 #+caption: ~HSlice~: Slice iterators, random range, string slice #+begin_src nim import strformat, typetraits let hs1 = 1 .. 3 echo fmt"hs1={hs1} is of type {$type(hs1)}" for n in hs1: echo n let hs2 = 'k' .. 'm' echo fmt"hs2={hs2} is of type {$type(hs2)}" for c in hs2: echo c import random echo "random num: ", rand(8 .. 9) let str = "abcdefghijk" echo str[2 .. 4] #+end_src #+RESULTS: code__hslice_1 #+begin_example hs1=(a: 1, b: 3) is of type HSlice[system.int, system.int] 1 2 3 hs2=(a: 'k', b: 'm') is of type HSlice[system.char, system.char] k l m random num: 9 cde #+end_example But by its definition, an HSlice can have elements of different types too: #+name: code__hslice_2 #+caption: ~HSlice~: Heterogeneous types #+begin_src nim import strformat, typetraits let hs3 = 1 .. 'z' hs4 = 0.99 .. "blah" echo fmt"hs3={hs3} is of type {$type(hs3)}" echo fmt"hs4={hs4} is of type {$type(hs4)}" #+end_src #+RESULTS: code__hslice_2 : hs3=(a: 1, b: 'z') is of type HSlice[system.int, system.char] : hs4=(a: 0.99, b: "blah") is of type HSlice[system.float64, system.string] As the [[https://nim-lang.org/docs/system.html#%5B%5D,string,HSlice%5BT,U%5D][string slice notation]] uses HSlice, and the HSlice elements can be of different types, below works too: #+name: code__hslice_3 #+caption: ~HSlice~: String slice with heterogeous type elements #+begin_src nim let str = "abcdefghijk" type MyEnum = enum Sixth = 5 Seventh echo str[3 .. Sixth] #+end_src #+RESULTS: code__hslice_3 : def Here are few examples of nested HSlices: #+name: code__hslice_4 #+caption: ~HSlice~: Nested #+begin_src nim import strformat, typetraits let hs5 = 777 .. 'd' .. "foo" hs6 = (777 .. 'd') .. "foo" # same as above hs7 = 5 .. 6 .. 'x' .. 'y' hs8 = ((5 .. 6) .. 'x') .. 'y' # same as above echo fmt"hs5={hs5} is of type {$type(hs5)}" echo fmt"hs6={hs6} is of type {$type(hs6)}" echo fmt"hs7={hs7} is of type {$type(hs7)}" echo fmt"hs8={hs8} is of type {$type(hs8)}" #+end_src #+RESULTS: code__hslice_4 : hs5=(a: (a: 777, b: 'd'), b: "foo") is of type HSlice[HSlice[system.int, system.char], system.string] : hs6=(a: (a: 777, b: 'd'), b: "foo") is of type HSlice[HSlice[system.int, system.char], system.string] : hs7=(a: (a: (a: 5, b: 6), b: 'x'), b: 'y') is of type HSlice[HSlice[HSlice[system.int, system.int], system.char], system.char] : hs8=(a: (a: (a: 5, b: 6), b: 'x'), b: 'y') is of type HSlice[HSlice[HSlice[system.int, system.int], system.char], system.char] As it can be seen above, things can get crazy too quickly, so use parentheses to break up the HSlices as needed -- Below is an HSlice of two different types of HSlices: #+name: code__hslice_5 #+caption: ~HSlice~: Nested, with parentheses #+begin_src nim import strformat, typetraits let hs9 = (5 .. 6) .. ('x' .. 'y') echo fmt"hs9={hs9} is of type {$type(hs9)}" #+end_src #+RESULTS: code__hslice_5 : hs9=(a: (a: 5, b: 6), b: (a: 'x', b: 'y')) is of type HSlice[HSlice[system.int, system.int], HSlice[system.char, system.char]] *** (Homogeneous) Slice :PROPERTIES: :CUSTOM_ID: slice :END: A [[https://nim-lang.org/docs/system.html#Slice][~Slice~]] is an alias for ~HSlice[T, T]~ i.e. it is a "heterogeneous" slice where both of the elements are of the same type. Below snippet shows that the ~HSlice[int, int]~ and ~Slice[int]~ are the same (as we can assign value from one of those types to another): #+name: code__slice_1 #+caption: ~Slice~: ~HSlice~ with same types #+begin_src nim import strformat, typetraits let s1: Slice[int] = 4 .. 6 echo fmt"s1={s1} is of type {$type(s1)}" let s2: HSlice[int, int] = s1 echo fmt"s2={s2} is of type {$type(s2)}" #+end_src #+RESULTS: code__slice_1 : s1=(a: 4, b: 6) is of type Slice[system.int] : s2=(a: 4, b: 6) is of type Slice[system.int] - Note :: Above, the type for ~s2~ is not printed correctly; it should have been ~HSlice[system.int, system.int]~. See [[#issue-7976][here]] for details. Attempting to assign an *HSlice* with different-type elements to a *Slice* will give compilation error: #+begin_src nim :eval no let s1: Slice[int] = 4 .. 'f' #+end_src #+begin_example nim_src_LLOTy4.nim(5, 22) Error: type mismatch: got but expected 'Slice[system.int]' #+end_example ** Arrays and Sequences [[https://nim-lang.org/docs/manual.html#types-array-and-sequence-types][Array and Sequence Types]] *** Arrays :PROPERTIES: :CUSTOM_ID: arrays :END: - Arrays are a *homogeneous* type, meaning that each element in the array has the same type. - Arrays always have a fixed length which has to be *specified at compile time* (except for [[* Open Arrays][/open arrays/]]). The array type specification needs 2 things: *size* of the array, and the *type* of elements contained in that array using ~array[SIZE, TYPE]~. The SIZE can be specified as a range like ~N .. M~, or ~(M-N)+1~ for short. The below example shows how an array of 6 integers can be declared and assigned using two different methods. #+begin_src nim let i_arr1: array[0 .. 5, int] = [1, 2, 3, 4, 5, 6] i_arr2: array[6, int] = [7, 8, 9, 10, 11, 12] echo i_arr1 echo i_arr2 #+end_src #+RESULTS: : [1, 2, 3, 4, 5, 6] : [7, 8, 9, 10, 11, 12] The ~[ .. ]~ portion to the right of the assign operator (~=~) is called the /array constructor/. - Built-in procs ~low()~ and ~high()~ return the lower and upper bounds of an array. - Arrays can also have a non-zero starting index; see the below example. - ~len()~ returns the array length. #+begin_src nim let i_arr: array[6 .. 11, int] = [1, 2, 3, 4, 5, 6] echo "min_index = ", low(i_arr), ", max_index = ", high(i_arr), ", i_arr = ", i_arr, ", length = ", len(i_arr) #+end_src #+RESULTS: : min_index = 6, max_index = 11, i_arr = [1, 2, 3, 4, 5, 6], length = 6 **** "Typed" Arrays Now, if you need to create a lot of arrays of the type ~array[6 .. 11, int]~, updating their size and/or element type can become painful and error-prone. So the convention is to first create a ~type~ for arrays, and then declare variables using that custom type. Below example is a re-written version of the above example using "typed" arrays. #+begin_src nim type IntArray = array[6 .. 11, int] let i_arr: IntArray = [1, 2, 3, 4, 5, 6] echo "min_index = ", low(i_arr), ", max_index = ", high(i_arr), ", i_arr = ", i_arr, ", length = ", len(i_arr) #+end_src #+RESULTS: : min_index = 6, max_index = 11, i_arr = [1, 2, 3, 4, 5, 6], length = 6 **** Inferred Array Types Just as you can do ~let foo = "abc"~ instead of ~let foo: string = "abc"~, you can let Nim infer the array types too. #+begin_src nim import strformat, typetraits let arr1 = [1.0, 2, 3, 4] arr2 = ['a', 'b', 'c', 'd'] arr3 = ["a", "bc", "def", "ghij", "klmno"] echo fmt"arr1 is of type {arr1.type.name} with value {arr1}" echo fmt"arr2 is of type {arr2.type.name} with value {arr2}" echo fmt"arr3 is of type {arr3.type.name} with value {arr3}" #+end_src #+RESULTS: : arr1 is of type array[0..3, float64] with value [1.0, 2.0, 3.0, 4.0] : arr2 is of type array[0..3, char] with value ['a', 'b', 'c', 'd'] : arr3 is of type array[0..4, string] with value ["a", "bc", "def", "ghij", "klmno"] **** Array elements need to be of the same type In the above example, ~let arr1 = [1.0, 2, 3, 4]~ worked because Nim inferred 2, 3 and 4 to be floats 2.0, 3.0 and 4.0. But if you try to mix-and-match types in array elements where they cannot get coerced to the same type, you get an error. #+begin_src nim :eval no let arr1 = [1.0, 2, 3, 4, 'a'] #+end_src #+begin_example nim_src_PZd0Ji.nim(5, 25) Error: type mismatch: got but expected 'float64 = float' #+end_example **** Two dimensional Arrays See [[#procs-operators]] for an example of 2-D arrays. **** Arrays with enum as length :PROPERTIES: :CUSTOM_ID: enum-length-arrays :END: Array types can be declared as ~array[, ]~ too. In this case, the array length will be set equal to the number of values in that enum type, and so it has to be assigned exactly that many elements. #+begin_src nim type Foo = enum oneHundred twoHundred threeHundred proc dict(f: Foo) = const fooInt: array[Foo, int] = [100, 200, 300] fooString: array[Foo, string] = ["one hundred", "two hundred", "three hundred"] echo fooInt[f], " ", fooString[f] dict(twoHundred) #+end_src #+RESULTS: : 200 two hundred As seen from the above example, such "enum-length arrays" can serve as "dictionaries" to provide a one-to-one translation from each enum value to something else. If such "enum-length arrays" are not assigned the required number of elements (equal to the number of enum values), you get a compile error. #+begin_src nim :eval no type Foo = enum oneHundred, twoHundred, threeHundred proc dict(f: Foo) = const fooInt: array[Foo, int] = [100, 200] echo fooInt[f] #+end_src #+begin_example nim_src_KdnP9k.nim(11, 31) Error: type mismatch: got but expected 'array[Foo, int]' #+end_example Here's another example from [[https://nim-by-example.github.io/arrays/][Nim By Example -- Arrays]]: #+begin_src nim type PartsOfSpeech = enum speechPronoun, speechVerb, speechArticle, speechAdjective, speechNoun, speechAdverb let partOfSpeechExamples: array[PartsOfSpeech, string] = [ "he", "reads", "the", "green", "book", "slowly" ] echo partOfSpeechExamples #+end_src #+RESULTS: : ["he", "reads", "the", "green", "book", "slowly"] *** Sequences :PROPERTIES: :CUSTOM_ID: sequences :END: Sequences are similar to arrays but of dynamic length which may change during runtime (like strings). - In the sequence constructor ~@[1, 2, 3]~, the ~[]~ portion is actually the /array constructor/, and ~@~ is the /array to sequence/ operator. - Just like other Nim assignments, the sequence type does not need to be specified if it is directly assigned a value --- the sequence type is inferred by Nim in that case. See [[* Specifying Types]] for more information. #+begin_src nim import strformat, typetraits let i_seq1:seq[int] = @[1, 2, 3, 4, 5, 6] i_seq2 = @[7.0, 8, 9] echo fmt"i_seq1 is of type {i_seq1.type.name} with value {i_seq1}" echo fmt"i_seq2 is of type {i_seq2.type.name} with value {i_seq2}" #+end_src #+RESULTS: : i_seq1 is of type seq[int] with value @[1, 2, 3, 4, 5, 6] : i_seq2 is of type seq[float64] with value @[7.0, 8.0, 9.0] - One can append elements to a sequence with the ~add()~ proc or the ~&~ operator, - ~pop()~ can be used to remove (and get) the last element of a sequence. - Built-in proc ~high()~ returns the upper bound of a sequence. - ~low()~ also works for a sequence, but it will always return 0. - ~len()~ returns the sequence length. #+begin_src nim import strformat, typetraits var i_seq = @[1, 2, 3] echo fmt"i_seq is of type {i_seq.type.name} with value {i_seq}" echo fmt"i_seq = {i_seq}, length = {i_seq.len}, last elem = {i_seq[i_seq.high]}" echo "Adding 100 using `add' .." i_seq.add(100) echo fmt" i_seq = {i_seq}, length = {i_seq.len}, last elem = {i_seq[i_seq.high]}" echo "Adding 200 using `&' .." i_seq = i_seq & 200 echo fmt" i_seq = {i_seq}, length = {i_seq.len}, last elem = {i_seq[i_seq.high]}" echo "Popping the last seq element using `pop' .." let popped_elem = i_seq.pop echo fmt" popped_elem = {popped_elem}" echo fmt" i_seq = {i_seq}, length = {i_seq.len}, last elem = {i_seq[i_seq.high]}" #+end_src #+RESULTS: : i_seq is of type seq[int] with value @[1, 2, 3] : i_seq = @[1, 2, 3], length = 3, last elem = 3 : Adding 100 using `add' .. : i_seq = @[1, 2, 3, 100], length = 4, last elem = 100 : Adding 200 using `&' .. : i_seq = @[1, 2, 3, 100, 200], length = 5, last elem = 200 : Popping the last seq element using `pop' .. : popped_elem = 200 : i_seq = @[1, 2, 3, 100], length = 4, last elem = 100 *** Reversing Sequences Use ~reversed~ proc from the stdlib [[https://nim-lang.org/docs/algorithm.html][algorithm]]. It takes in arrays, sequences and strings, but always returns sequences. #+begin_src nim import algorithm echo reversed([1, 2, 3]) # array echo reversed(@[1, 2, 3]) # sequence echo reversed("abc") # string #+end_src #+RESULTS: : @[3, 2, 1] : @[3, 2, 1] : @['c', 'b', 'a'] Note that the string "abc" converts to a reversed sequence of chars composing that string. If you want a reversed string, just ~join~ those chars again: #+begin_src nim import algorithm, strutils echo reversed("abc").join("") #+end_src #+RESULTS: : cba *** Open Arrays [[https://nim-lang.org/docs/manual.html#types-open-arrays][Open arrays]] #+begin_note Open array types can be assigned to only parameters of procedures that receive values of ~seq~ or ~array~ types. #+end_note - Open array types are like /dynamic arrays/, and need to be specified with only the *type* of the elements that the array is going to contain. - This array type can be used *only for ~parameters~ in procedures*. - So if you do ~var foo: openArray[int]~ or ~let foo: openArray[int] = [1,2,3]~ (inside a proc or outside), you will get /Error: invalid type: 'openarray[int]' for var/ or /Error: invalid type: 'openarray[int]' for let/. - The ~openArray~ type cannot be nested: multidimensional openarrays are not supported. - Built-in proc ~high()~ returns the upper bound of an open array too. - ~low()~ also works for an open array, but it will always return 0. - ~len()~ returns the open array length. #+begin_src nim import strformat, typetraits proc testOpenArray(x: openArray[int]) = echo fmt"x = {x} (type: {x.type.name}, length = {x.len}, max index = {x.high}" let some_seq = @[1, 2, 3] some_arr = [1, 2, 3] echo "Passing a seq .." testOpenArray(some_seq) echo "Passing an array .." testOpenArray(some_arr) #+end_src #+RESULTS: : Passing a seq .. : x = [1, 2, 3] (type: openarray[int], length = 3, max index = 2 : Passing an array .. : x = [1, 2, 3] (type: openarray[int], length = 3, max index = 2 #+begin_note If using a Nim /devel/ version older than {{{nimcommit(b4626a220b)}}}, use ~echo repr(x)~ to print an open array ~x~ --- plain old ~echo x~ or ~fmt~ like in the above example won't work. See [[#dollar-not-implemented-for-openarray]] for details. #+end_note Open arrays serve as a convenience type for procedure parameters so that: - We don't need to explicitly mention the types of the passed in array (or sequence) values. - For example, you can have a proc parameter of type ~openArray[int]~, and have it accept all of these types -- ~array[5, int]~, ~array[1000, int]~, ~seq[int]~.. /just that the array|seq|openArray elements need to be of the exact same *type*.. ~int~ in this case/. - The proc can receive an array of any length as long as they contain the same type elements as defined in the ~openArray[TYPE]~. Thanks to {{{nuser(r3d9u11)}}} for the below example ([[https://forum.nim-lang.org/t/3872#24102][ref]]). #+begin_src nim type MyArr = array[3, int] let a3: MyArr = [10, 20, 30] a4 = [10, 20, 30, 40] # Compiler doesn't allow passing arrays with any other length proc countMyArr(a: MyArr): int = for i in 0 .. a.high: result += a[i] echo "countMyArray(a3) = ", countMyArr(a3) # countMyArray(a4) # will fail # Compiler doesn't allow passing arrays with any other length proc countExplicitArrayType(a: array[3, int]): int = for i in 0 .. a.high: result += a[i] echo "countExplicitArrayType(a3) = ", countExplicitArrayType(a3) # countExplicitArrayType(a4) # will fail # Compiler allows passing arrays with different lengths proc countOpenArray(a: openArray[int]): int = for i in 0 .. a.high: result += a[i] echo "countOpenArray(a3) = ", countOpenArray(a3) echo "countOpenArray(a4) = ", countOpenArray(a4) #+end_src #+RESULTS: : countMyArray(a3) = 60 : countExplicitArrayType(a3) = 60 : countOpenArray(a3) = 60 : countOpenArray(a4) = 100 Above, you can see that ~countMyArr~ and ~countExplicitArrayType~ procs can accept only /int/ arrays of length 3. Attempting to pass the 4-element /int/ array ~a4~ to those will give an error like this: #+begin_example nim_src_gHCsrO.nim(20, 61) Error: type mismatch: got but expected one of: proc countExplicitArrayType(a: array[3, int]): int expression: countExplicitArrayType(a4) #+end_example But ~countOpenArray~ proc is not limited by the input array length --- It can accept an /int/ array of /any/ length. So ~countOpenArray(a4)~ works just fine. **** Open Array Limitations ***** Modifying Open array variables inside procedures From my brief experimentation, you cannot modify the open arrays easily. For example, below does not work. /I thought that as a passed in sequence got converted to an openarray type, the reverse should be possible too./ But that's not the case .. #+begin_src nim :eval no proc foo(i_oa: openArray[int]) = var i_seq: seq[int] = i_oa i_seq.add(100) echo "input open array: ", i_oa echo "that open array modified to a sequence: ", i_seq foo(@[10, 20, 30, 40]) #+end_src Above gives the error: #+begin_example nim_src_dqL4Jb.nim(5, 28) Error: type mismatch: got but expected 'seq[int]' #+end_example Here is a workaround: #+begin_src nim proc foo(i_oa: openArray[int]) = var i_seq: seq[int] for i in i_oa: # copying the input openarray to seq element by element i_seq.add(i) i_seq.add(100) echo "input open array: ", i_oa echo "that open array modified to a sequence: ", i_seq foo(@[10, 20, 30, 40]) #+end_src #+RESULTS: : input open array: [10, 20, 30, 40] : that open array modified to a sequence: @[10, 20, 30, 40, 100] ***** FIXED ~$~ is not implemented for /open arrays/ CLOSED: [2018-06-01 Fri 15:25] :PROPERTIES: :CUSTOM_ID: dollar-not-implemented-for-openarray :END: Thanks to {{{guser(data-man)}}}, this issue is now fixed in {{{nimcommit(b4626a220b)}}}! #+begin_src nim import strformat proc foo(x: openArray[int]) = echo fmt"{x}" foo([1, 2, 3]) #+end_src #+RESULTS: : [1, 2, 3] ****** Older failure As ~$~ isn't implement for open arrays, ~fmt~ wouldn't work for these either -- {{{nimissue(7940)}}}. #+begin_src nim :eval no proc testOpenArray(x: openArray[int]) = echo x #+end_src Above gives this error: #+begin_example nim_src_ghd2T5.nim(5, 8) Error: type mismatch: got but expected one of: proc `$`(x: string): string proc `$`(s: WideCString): string proc `$`[T: tuple | object](x: T): string proc `$`(x: uint64): string proc `$`(x: int64): string proc `$`[T, IDX](x: array[IDX, T]): string proc `$`[Enum: enum](x: Enum): string proc `$`(w: WideCString; estimate: int; replacement: int = 0x0000FFFD): string proc `$`[T](x: set[T]): string proc `$`[T](x: seq[T]): string proc `$`(x: int): string proc `$`(x: cstring): string proc `$`(x: bool): string proc `$`(x: float): string proc `$`(x: char): string expression: $(x) #+end_example Until that gets fixed, the workaround is to use ~repr~ to print open arrays. *** Seq vs Array vs Open Array Below are few useful comments from [[https://forum.nim-lang.org/t/3872][this Nim forum thread]]. While crediting the comment authors, I have taken the liberty to copy-edit those and add my own emphasis. - {{{nuser(Stefan_Salewski)}}} [[https://forum.nim-lang.org/t/3872#24095][#]] :: *Seqs* are generally fine when performance and code size is not critical. As ~seq~ data structure contains a pointer to the data elements, we have some indirection, which decreases performance. *Arrays* are used when number of elements is known at compile time. They offer the maximum performance. Nim Arrays are value (i.e. not reference) objects, living on the stack (/see [[#heap-stack]]/) if used inside of procs. So a very direct element access is possible. Basically element access is only calculation of an offset, which is the product of element size and index value. If index is constant, then this offset is known at compile time, and so the access is as fast as that of a plain proc variable. /An example is a chess program where we have the 64 fields. So we generally would use a array of 64 elements, not a seq. (Well, beginners may use a two dimensional array of 8 x 8, but then we have two indices, which again increases access time. A plain array is OK for this game, as we can increase index by one when we move a piece to the left, and increase index by 8 when we move forward.)/ *OpenArrays* is just a proc *parameter* type that can accept arrays and seqs. Whenever a proc can be used with ~array~ or ~seq~ parameters, the ~openArray~ parameter type should be used. /The word "OpenArray" is indeed a bit strange; it was used in the Wirthian languages Modula and Oberon, we have no better term currently./ - {{{nuser(r3d9u11)}}} [[https://forum.nim-lang.org/t/3872#24102][#]] :: (Provided a very useful example which I expanded upon in [[* Open Arrays]]). ** Python-like Dictionaries :PROPERTIES: :CUSTOM_ID: dictionaries :END: - See [[#tables]]. - See [[#critbits]]. ** Tuples - [[https://nim-lang.org/docs/tut1.html#advanced-types-tuples][Nim Tutorial -- Tuples]] A *tuple* type defines various named fields and an order of the fields. The constructor ~()~ can be used to construct tuples. The order of the fields in the constructor must match the order in the tuple's definition. The assignment operator for tuples copies each component. *** Defining tuples - Assigning the /tuple/ type directly, *anonymously*. For /anonymous tuples/ don't use the ~tuple~ keyword and square brackets. #+begin_src nim let person: (string, int) = ("Peter", 30) echo person #+end_src #+RESULTS: : (Field0: "Peter", Field1: 30) - Assigning the /tuple/ type directly, with *named fields*. #+begin_src nim let person: tuple[name: string, age: int] = ("Peter", 30) echo person #+end_src #+RESULTS: : (name: "Peter", age: 30) - First defining a custom /tuple/ type, and then using that. This is useful if you don't wan't to copy/paste a verbose /tuple/ type like ~tuple[name: string, age: int]~ at multiple places.. that approach is also very error-prone and painful if you ever need to refactor that tuple type throughout your code. #+begin_src nim type Person = tuple[name: string, age: int] let person1: Person = (name: "Peter", age: 30) # skipping the field names during assignment works too, but this could be less # readable. person2: Person = ("Mark", 40) echo person1 echo person2 #+end_src #+RESULTS: : (name: "Peter", age: 30) : (name: "Mark", age: 40) - Alternative way of defining that same custom tuple type. #+begin_src nim type Person = tuple name: string age: int let person: Person = (name: "Peter", age: 30) echo person #+end_src #+RESULTS: : (name: "Peter", age: 30) *** Accessing tuple fields - The notation ~t.field~ is used to access a named tuple's field. - Another notation is ~t[i]~ to access the ~i~'th field. Here ~i~ must be a constant integer. This works for both anonymous and named tuples. **** Anonymous tuples #+begin_src nim import strformat, typetraits let person: (string, int) = ("Peter", 30) echo fmt"Tuple person of type {person.type.name} = {person}" echo person[0] echo person[1] #+end_src #+RESULTS: : Tuple person of type (string, int) = (Field0: "Peter", Field1: 30) : Peter : 30 **** Named tuples #+begin_src nim import strformat, typetraits type Person = tuple name: string age: int let person: Person = ("Peter", 30) echo fmt"Tuple person of type {name(person.type)} = {person}" echo person[0] echo person.name echo person[1] echo person.age #+end_src #+RESULTS: : Tuple person of type Person = (name: "Peter", age: 30) : Peter : Peter : 30 : 30 /See [[#issue-7975]] on why I am using ~name(person.type)~ in the above code snippet instead of ~person.type.name~./ **** Check if a field exists :PROPERTIES: :CUSTOM_ID: tuple-field-exists :END: Use [[#compiles][~compiles~]]. If the argument is a tuple variable with valid field reference, it will return ~true~ -- [[https://gitter.im/nim-lang/Nim?at=5ba3d327e6046343f38f415f][Ref]]. ***** Named tuples #+name: code__named_tuples_field_checks__var #+caption: Checking if fields exist in a named-tuple variable #+begin_src nim type Person = tuple name: string age: int let p: Person = ("Peter", 30) echo compiles(p.age) echo compiles(p.foo) # invalid field echo compiles(p[0]) echo compiles(p[1]) echo compiles(p[2]) # invalid field.. tuple has only 2 fields #+end_src #+RESULTS: code__named_tuples_field_checks__var : true : false : true : true : false Above, the tuple variable ~p~ is passed to ~compiles~. You can instead pass the tuple /type/ ~Person~ too. This is useful if you want to check if a tuple field exists before creating a variable of its type. But then, *only named field check works* -- [[https://gitter.im/nim-lang/Nim?at=5ba3d6dcbe4f300626c22168][Ref]]. #+name: code__named_tuples_field_checks__type #+caption: Checking if fields exist in a named-tuple *type* #+begin_src nim type Person = tuple name: string age: int echo compiles(Person.age) echo compiles(Person.foo) # invalid field echo compiles(Person[0]) # incorrectly returns 'false', compared to compiles(p[0]) in previous snippet #+end_src #+RESULTS: code__named_tuples_field_checks__type : true : false : false #+begin_note Do not use a tuple /type/ identifier with field index reference when checking if that field exists. #+end_note Here's why ~compiles(Person.age)~ works, but ~compiles(Person[0])~ doesn't -- ~Person.age~ returns its type, ~int~, but ~Person[0]~ causes compilation failure: #+begin_quote nim_src_AVBMhm.nim(10, 12) Error: no generic parameters allowed for Person #+end_quote #+begin_src nim import typetraits type Person = tuple name: string age: int echo Person.age # echo Person[0] # causes compilation error #+end_src #+RESULTS: : int The compilation error makes sense, because a tuple type can be [[* Generic Tuples][/generic/]] too! ***** Anonymous tuples #+name: code__anon_tuples_field_checks__var #+caption: Checking if fields exist in an anonymous-tuple variable #+begin_src nim let q: (string, int, float) = ("abc", 42, 3.117) echo compiles(q[0]) echo compiles(q[1]) echo compiles(q[2]) echo compiles(q[3]) # invalid field.. tuple has only 3 fields #+end_src #+RESULTS: code__anon_tuples_field_checks__var : true : true : true : false For the reason explained in above section, it is possible to check if a tuple field exists, by using its index, but only when using the tuple variable identifier, and not its type. **** Finding number of tuple fields :PROPERTIES: :CUSTOM_ID: tuple-field-count :END: As suggested by {{{guser(Vindaar)}}}, [[https://nim-lang.org/docs/typetraits.html#arity,typedesc][~arity~]] from /typetraits/ can be used to get the number of fields in a tuple -- [[https://gitter.im/nim-lang/Nim?at=5ba3d97b913ba7799b1c4255][Ref]]. #+name: code__number_of_tuple_fields #+caption: Number of tuple fields #+begin_src nim import strformat, typetraits type Person = tuple name: string age: int let p: Person = ("Peter", 30) q: (string, int, float) = ("abc", 42, 3.117) echo fmt"Number of fields in p = {p.type.arity}, value = {p}" echo fmt"Number of fields in q = {q.type.arity}, value = {q}" #+end_src #+RESULTS: code__number_of_tuple_fields : Number of fields in p = 2, value = (name: "Peter", age: 30) : Number of fields in q = 3, value = (Field0: "abc", Field1: 42, Field2: 3.117) *** Tuple unpacking Tuples can be unpacked during variable assignment (and only then!). This can be handy to assign directly the fields of the tuples to individually named variables. An example of this is the ~splitFile~ proc from the ~os~ module which returns the /directory/, /name/ and /extension/ of a path at the same time. For tuple unpacking to work *you must use parentheses around the values you want to assign the unpacking to*, otherwise you will be assigning the same value to all the individual variables! For example: #+begin_src nim import os let path = "usr/local/nimc.html" (dir, name, ext) = splitFile(path) baddir, badname, badext = splitFile(path) echo "dir = ", dir echo "name = ", name echo "ext = ", ext echo "baddir = ", baddir echo "badname = ", badname echo "badext = ", badext #+end_src #+RESULTS: : dir = usr/local : name = nimc : ext = .html : baddir = (dir: "usr/local", name: "nimc", ext: ".html") : badname = (dir: "usr/local", name: "nimc", ext: ".html") : badext = (dir: "usr/local", name: "nimc", ext: ".html") *** Tuple type equality **** Comparing one named tuple to another Different *named* tuple-types are equivalent if they: 1. specify fields of the same type, and 2. of the same name in the same order. #+begin_src nim type Person = tuple[name: string, age: int] var person: Person person = (name: "Peter", age: 30) echo "person = ", person var teacher: tuple[name: string, age: int] = ("Mark", 42) echo "teacher = ", teacher # The following works because the field names and types are the same. person = teacher echo "person = ", person #+end_src #+RESULTS: : person = (name: "Peter", age: 30) : teacher = (name: "Mark", age: 42) : person = (name: "Mark", age: 42) /You don't need to declare a type for a tuple to use it./ -- like the example of ~teacher~ variable in the above example. Named tuples created with different field names will be considered different objects despite having same number of fields of the same types. #+begin_src nim :eval no var person: tuple[name: string, kids: int] = ("Peter", 1) let teacher: tuple[name: string, age: int] = ("Mark", 42) person = teacher #+end_src Above will give this error: #+begin_example nim_src_KDTO15.nim(6, 8) Error: type mismatch: got but expected 'tuple[name: string, kids: int]' #+end_example **** Comparing named tuple and anonymous tuple types From the following example, it looks like /anonymous/ tuples are considered to be of the same type as named tuples if: - they have the same number of fields, and - with the same type and order. #+begin_src nim import strformat, typetraits type Person = tuple[name: string, age: int] let personAnon1 = ("Peter", 30) var personNamed: Person personAnon2: (string, int) echo fmt"Tuple personAnon1 of type {personAnon1.type.name} = {personAnon1}" echo "Assigning an anonymous tuple to a named tuple .." personNamed = personAnon1 echo fmt"Tuple personNamed of type {name(personNamed.type)} = {personNamed}" echo "Assigning a named tuple to an anonymous tuple .." personAnon2 = personNamed echo fmt"Tuple personAnon2 of type {personAnon2.type.name} = {personAnon2}" #+end_src #+RESULTS: : Tuple personAnon1 of type (string, int) = (Field0: "Peter", Field1: 30) : Assigning an anonymous tuple to a named tuple .. : Tuple personNamed of type Person = (name: "Peter", age: 30) : Assigning a named tuple to an anonymous tuple .. : Tuple personAnon2 of type (string, int) = (Field0: "Peter", Field1: 30) #+begin_note When declaring new tuple variables by direct value assignment, don't forget to specify the tuple type! If you don't do so, that tuple will default to an /anonymous/ tuple. #+end_note From the above example, note that ~personAnon1~ didn't get the field names as it got assigned using a direct value without specifying the tuple type. *** Tuple comparison #+begin_src nim let NimVersionT = (major: NimMajor, minor: NimMinor, patch: NimPatch) echo NimVersionT assert (0, 18, 0) < NimVersionT assert (0, 19, 0) == NimVersionT assert (1, 0, 0) > NimVersionT #+end_src #+RESULTS: : (major: 0, minor: 19, patch: 0) *** Generic Tuples A tuple can be a /generic/ too! /Thanks to {{{guser(Vindaar)}}} and TheLemonMan on IRC to help me understand this -- [[https://gitter.im/nim-lang/Nim?at=5ba3dfc25df5194734eb11ec][Ref]]./ #+name: code__generic_tuple #+caption: A generic tuple #+begin_src nim type Person[N: static[int]] = tuple name: string childrenAges: array[N, int] let p0: Person[0] = ("Peter", []) p1: Person[1] = ("Peter", [1]) p2: Person[2] = ("Peter", [1, 2]) p3: Person[3] = ("Peter", [1, 2, 3]) echo p0 echo p1 echo p2 echo p3 #+end_src #+RESULTS: code__generic_tuple : (name: "Peter", childrenAges: []) : (name: "Peter", childrenAges: [1]) : (name: "Peter", childrenAges: [1, 2]) : (name: "Peter", childrenAges: [1, 2, 3]) ** TODO Objects - [[https://nim-lang.org/docs/tut2.html#object-oriented-programming-objects][Nim Tutorial -- Objects]] - See [[#object-types]] * TODO Pointers and References - [[https://nim-lang.org/docs/tut1.html#advanced-types-reference-and-pointer-types][Nim Tutorial -- Reference and Pointer Types]] - [[https://rosettacode.org/wiki/Pointers_and_references#Nim][Rosetta Code -- Pointers and Reference]] ** TODO Pointer (~ptr~) ** Dereferencing #+begin_src nim import strformat, typetraits type Foo = ref object x, y: float var f: Foo new f # Accessing the reference echo fmt"Type of f = {$type(f)}" echo fmt"{$type(f[])} f[] = {f[]}" f[].y = 12 echo fmt"{$type(f[])} f[] = {f[]}" # When accessing values the dereference operator [] can be left out. f.x = 13.5 echo fmt"{$type(f[])} f[] = {f[]}" #+end_src #+RESULTS: : Type of f = Foo : Foo:ObjectType f[] = (x: 0.0, y: 0.0) : Foo:ObjectType f[] = (x: 0.0, y: 12.0) : Foo:ObjectType f[] = (x: 13.5, y: 12.0) ** Heap and Stack :PROPERTIES: :CUSTOM_ID: heap-stack :END: - Credit :: Below explanation is taken entirely from [[https://www.reddit.com/r/nim/comments/7dm3le/tutorial_for_types_having_a_hard_time/dpz9vue/][this comment]] by {{{ruser(PMunch)}}}. First order of business heap vs. stack: Whenever you call a function it creates a stack frame. Stacks are simply said first in last out, so when a function call is done it pops it's frame from the stack, and you return to the previous frame. When you declare a variable inside a scope in is typically allocated on the stack. This means that when the function returns that part of memory is not in use anymore and the value is gone (technically it's still there, but we shouldn't try to access it). The heap on the other hand is quite a bit different. Items on the heap are allocated and live there until you deallocate them. In C this is done manually with calls like =malloc= and =free=. In Nim however, and C# for that matter, we have the garbage collector (or GC), this nifty thing reads through our memory and checks if anything still points to things allocated on the heap and if nothing is pointing to that which was previously allocated it gets free'd. This means that we won't leak memory as easily as you would in C as things that we've lost every reference to get's automatically cleaned. Okay, now that we know /where/ our stuff lives in memory, let's look at how Nim represents those things. Basic types are things like integers, floats, bools, and characters. Their size is known and static. Nim also calls strings a basic type, but those are not quite like the rest since they can change in size. Since our stack is first in last out it means that it makes sense to store everything in order. But storing things in order isn't possible if you want to change the size of something (for example appending to a string). So when we create numbers in our Nim code they will be stored on the stack, this is why you never need to free your numbers and don't have to set them to =nil= when you're done with them. So what are types? In Nim you can create aliases for types like ~type age = int~ this is just a way to say that =age= is an integer, and it will be treated like one for all intents and purposes. If we want to create collections of types to represent something particular we can create =objects=, don't think of these quite like objects in an object oriented language, think of them more like structs in C. Such objects are simply a collection of values. So in Nim when we create an object it will live with us on the stack. If we return an object it will be copied to our caller, and if we insert it into a data structure it will also be copied. While this might be practical in many cases (even offering a speed benefit if done right) we often don't want to copy our large objects around. This is when allocating on the heap comes into play. If we define our type as a =ref object= it means that the type is actually a reference to an object. I'll come back to the difference between a reference and a pointer later, but for now just remember that a reference is the memory location of an object on the heap. This means that if we return a =ref object= it means that we're only returning the /memory address/ of that object, not copying the object itself. This also means that if we insert it into a data structure only the reference to the object is inserted. Whenever you see =new SomeObject= it means that it allocates memory for that object on the heap, and gives us a reference to this object. If we had simply done =var myObject: SomeObject= and =SomeObject= was defined as a =ref object= we would only have a reference on our stack, so trying to access it would crash saying we had an "Illegal storage access". This is because Nim defaults our value to =nil=, and no-one is allowed to access memory area 0. So imagine we had an object that contained the height, the weight, and the age of a person. That could be represented by three integers. If we wanted to return this object it would mean copying all those three values to our caller. If we defined it as a reference, we would only pass one integer, the position in memory (on the heap) where we stored the three others. Conveniently this also means that if one functions modifies a value in a referenced object, that change would be visible for all other functions using the same reference (since they all point to the same place in memory). This is practical for example if you want to make one list sorted by age, one by height, and the third by weight. Instead of copying our person three times, we could just use three references, one in each list. So now that we know /where/ and /how/ are values are stored we can look at the difference between a pointer and a reference. In pure Nim code you would typically only use references, these are what every call to =new= creates and what goes on behind the scenes most of the time when working with strings. A reference is also called a managed pointer, it simply means that Nim manages this area of memory, and that it will be automatically free'd for us by the garbage collector when Nim sees that we're not using it any longer. Pointers on the other hand are /unmanaged/ meaning that Nim doesn't try to do anything with the memory behind that pointer, most of the time you won't even know what is there. The reason there are pointers in Nim is mostly to interface with C. In C every time you want objects on the heap you need to manually =malloc= and =free= them. Many C libraries work by passing around a pointer to a structure containing some state and all good libraries have some way of dealing with this memory. Typically you do it by calling some initialisation function when you begin and then some cleanup function when you are done. In Nim we might want to use a C library such as that, but since we might loose the reference in our own code while the library still keeps a reference somewhere which Nim doesn't know about we can't have a reference to it as Nim would garbage collect it. So instead we have pointers. * Regular Expressions :PROPERTIES: :CUSTOM_ID: regex :END: These apply as of today (<2018-08-23 Thu>): - If you have PCRE library on your system, use the [[https://nim-lang.org/docs/re.html][~re~]] stdlib. - PCRE syntax is used when using this library. See ~man pcresyntax~ from your terminal ([[http://man7.org/linux/man-pages/man3/pcresyntax.3.html][online]]). - Else, use [[https://github.com/nitely/nim-regex][~nix-regex~]] (needs to be installed using ~nimble install regex~). - [[https://nitely.github.io/nim-regex/][~regex~ documentation]] #+begin_note Don't use the [[https://nim-lang.org/docs/nre.html][~nre~]] stdlib, it's *deprecated*. -- [[https://gitter.im/nim-lang/Nim?at=5b7edf60f86b741b05b1d44b][Ref]] #+end_note ** Using ~re~ :PROPERTIES: :CUSTOM_ID: re-lib :END: #+name: code__re_replace_example #+caption: ~re.replace~ example #+begin_src nim import re let text = """Mary had a little lamb.""" rgx = re"(?i)mary" echo text.replace(rgx, "Mama lamb") #+end_src #+RESULTS: code__re_replace_example : Mama lamb had a little lamb. ** Using ~regex~ :PROPERTIES: :CUSTOM_ID: regex-lib :END: Below is the same example as in code snippet [[code__re_replace_example]], including the regular expression syntax, except that ~import regex~ is done instead of ~import re~. #+name: code__regex_replace_example #+caption: ~regex.replace~ example #+begin_src nim import regex let text = """Mary had a little lamb.""" rgx = re"(?i)mary" echo text.replace(rgx, "Mama lamb") #+end_src #+RESULTS: code__regex_replace_example : Mama lamb had a little lamb. * Command line parameters - [[https://devdocs.io/nim/os#paramCount,][~paramCount~]] returns the number of command line parameters given to the application. - [[https://devdocs.io/nim/os#commandLineParams,][~commandLineParams~]] returns the command line parameters. - Do ~import os~ before using any of the above functions. #+begin_src nim :cmdline foo bar "zoo car" # Arguments passed: foo bar "zoo car" import os, strformat let numParams = paramCount() params = commandLineParams() echo "Number of command line params: ", numParams echo "Command line params: ", params for n in 0 ..< numParams: echo fmt"Param {n+1} = {params[n]}" #+end_src #+RESULTS: : Number of command line params: 3 : Command line params: @["foo", "bar", "zoo car"] : Param 1 = foo : Param 2 = bar : Param 3 = zoo car For serious longopt and shortopt command line parameter parsing using the [[https://github.com/c-blake/cligen][*cligen*]] library. * Terminal ** Hello World animation - Credit :: Below code is taken from [[https://www.reddit.com/r/nim/comments/8fi5ce/the_current_top_post_on_rprogrammerhumor/dy3rg26/][this Reddit comment]]. {{{asciinema(184476)}}} #+begin_src nim :tangle code/hello_world_animation.nim :mkdirp yes :eval no import os, random, strutils, terminal randomize() # Initialize the RNG with a new seed each run hideCursor() # Hide the cursor during runtime var target, output: string if paramCount() >= 1: target = paramStr(1) else: target = "Hello, world" output = spaces(target.len) for i, x in target: while x != output[i]: output[i] = chr(rand(32 .. 126)) # Instead of writing a bunch of new lines, we can just write to the same # line repeatedly. eraseLine() stdout.write(output) stdout.flushFile() # Force writing to console every iteration sleep(3) # milliseconds # Add a new line at the end of program execution to keep the terminal tidy. echo "" showCursor() # Bring the cursor back #+end_src * Shell ** Environment Variables - Use ~putEnv~ to set environment variable - Use ~getEnv~ to get environment variable #+begin_src nim import os let envVar = "NIM_TEMP" envVarVal = "foo" putEnv(envVar, envVarVal) echo getEnv(envVar) #+end_src #+RESULTS: : foo * File paths ** Nim StdLib path Below code is taken from [[https://github.com/nim-lang/Nim/blob/964f1ff7a77e0cc1c73f12d02969421c36d32e1c/compiler/nimeval.nim#L78-L90][~compiler/nimeval.nim~]] in Nim source code. #+begin_src nim :noweb-ref findNimStdLib :exports none :eval no import os proc findNimStdLib*(): string = ## Tries to find a path to a valid "system.nim" file. ## Returns "" on failure. try: let nimexe = os.findExe("nim") if nimexe.len == 0: return "" result = nimexe.splitPath()[0] /../ "lib" if not fileExists(result / "system.nim"): when defined(unix): result = nimexe.expandSymlink.splitPath()[0] /../ "lib" if not fileExists(result / "system.nim"): return "" except OSError, ValueError: return "" #+end_src #+begin_src nim :noweb yes <> echo findNimStdLib() #+end_src #+RESULTS: : /home/kmodi/stowed/bin/../../stow/pkgs/nim/devel/lib ** File base name #+begin_src nim import os let filePath = "tests/test1.org" var (dir, basename, ext) = splitFile(filePath) echo "dir = ", dir echo "basename = ", basename echo "ext = ", ext #+end_src #+RESULTS: : dir = tests : basename = test1 : ext = .org * File handling ** Writing files #+begin_src nim :eval no let fileName = "/tmp/foo/bar/zoo.txt" dataStr = "abc" writeFile(fileName, dataStr) #+end_src The directory containing the ~fileName~ in the above example has to exist, else you get the ~IOError~ exception: #+begin_example Error: unhandled exception: cannot open: /tmp/foo/bar/zoo.txt [IOError] #+end_example If you want to force-create a non-existing directory, you can do: #+begin_src nim import os, strformat let fileName = "/tmp/foo/bar/zoo.txt" dataStr = "abc" (dir, _, _) = splitFile(fileName) removeDir("/tmp/foo/bar/") echo fmt"{dir} exists? {dirExists(dir)}" if (not dirExists(dir)): echo fmt" creating {dir} .." createDir(dir) echo fmt"{dir} exists now? {dirExists(dir)}" writeFile(fileName, dataStr) #+end_src #+RESULTS: : /tmp/foo/bar exists? false : creating /tmp/foo/bar .. : /tmp/foo/bar exists now? true ** File Permissions *** Get/read file permissions #+begin_src nim import os let fileName = "/tmp/foo/bar/zoo.txt" dataStr = "abc" (dir, _, _) = splitFile(fileName) if (not dirExists(dir)): createDir(dir) writeFile(fileName, dataStr) let filePerm = getFilePermissions(fileName) echo filePerm #+end_src #+RESULTS: : {fpUserWrite, fpUserRead, fpGroupRead, fpOthersRead} *** Octal string to ~set[FilePermission]~ #+begin_src nim import os proc parseFilePermissions(octals: string): set[FilePermission] = ## Converts the input permissions octal string to a Nim set for FilePermission type. # https://devdocs.io/nim/os#FilePermission var perm: set[FilePermission] let readPerms = @[fpUserRead, fpGroupRead, fpOthersRead] writePerms = @[fpUserWrite, fpGroupWrite, fpOthersWrite] execPerms = @[fpUserExec, fpGroupExec, fpOthersExec] for idx, o in octals: if o != '0': if o in {'4', '5', '6', '7'}: perm = perm + {readPerms[idx]} if o in {'2', '3', '6', '7'}: perm = perm + {writePerms[idx]} if o in {'1', '3', '5', '7'}: perm = perm + {execPerms[idx]} result = perm import random, strformat randomize(1) for _ in 0 .. 10: let perm = fmt"{rand(7)}{rand(7)}{rand(7)}" echo perm, " = ", parseFilePermissions(perm) #+end_src #+RESULTS: #+begin_example 112 = {fpUserExec, fpGroupExec, fpOthersWrite} 114 = {fpUserExec, fpGroupExec, fpOthersRead} 436 = {fpUserRead, fpGroupExec, fpGroupWrite, fpOthersWrite, fpOthersRead} 752 = {fpUserExec, fpUserWrite, fpUserRead, fpGroupExec, fpGroupRead, fpOthersWrite} 727 = {fpUserExec, fpUserWrite, fpUserRead, fpGroupWrite, fpOthersExec, fpOthersWrite, fpOthersRead} 101 = {fpUserExec, fpOthersExec} 236 = {fpUserWrite, fpGroupExec, fpGroupWrite, fpOthersWrite, fpOthersRead} 522 = {fpUserExec, fpUserRead, fpGroupWrite, fpOthersWrite} 425 = {fpUserRead, fpGroupWrite, fpOthersExec, fpOthersRead} 273 = {fpUserWrite, fpGroupExec, fpGroupWrite, fpGroupRead, fpOthersExec, fpOthersWrite} 304 = {fpUserExec, fpUserWrite, fpOthersRead} #+end_example * Exceptions ** Exception Hierarchy https://devdocs.io/nim/manual#exception-handling-exception-hierarchy All *exceptions* are /objects/ of type ~Exception~. - ~Exception~ - ~AccessViolationError~ - ~ArithmeticError~ - ~DivByZeroError~ - ~OverflowError~ - ~AssertionError~ - ~DeadThreadError~ - ~FloatingPointError~ - ~FloatDivByZeroError~ - ~FloatInexactError~ - ~FloatInvalidOpError~ - ~FloatOverflowError~ - ~FloatUnderflowError~ - ~FieldError~ - ~IndexError~ - ~ObjectAssignmentError~ - ~ObjectConversionError~ - ~ValueError~ - ~KeyError~ - ~ReraiseError~ - ~RangeError~ - ~OutOfMemoryError~ - ~ResourceExhaustedError~ - ~StackOverflowError~ - ~SystemError~ - ~IOError~ - ~OSError~ - ~LibraryError~ ** Custom Exceptions [[https://forum.nim-lang.org/t/2863/1#17817][ref]] #+begin_src nim :eval no type MyError = object of Exception #+end_src ** Raising Exceptions #+begin_src nim :eval no type MyError = object of Exception raise newException(MyError, "details about what went wrong") #+end_src Evaluating above will give: #+begin_example Error: unhandled exception: details about what went wrong [MyError] #+end_example ** Handling/catching Exceptions Above example had unhandled exceptions. You must always handle (my rule) exceptions and do something about it; even if it means simply printing out the exception error using ~getCurrentExceptionMsg()~. #+begin_src nim :eval no type MyError = object of Exception try: raise newException(MyError, "details about what went wrong") except MyError: stderr.writeLine "Error: ", getCurrentExceptionMsg() quit 1 # Quit with error exit code #+end_src Evaluating above will print this on /stderr/: #+begin_example Error: details about what went wrong #+end_example ~getCurrentExceptionMsg()~ returns only the ~msg~ parameter from the [[https://devdocs.io/nim/system#Exception][~Exception~ object]]. If you need to get the /Exception/ ~name~ too, use ~getCurrentException()~ instead, which return a value of type ~Exception~, and then print its ~name~ as shown in the below example. #+begin_src nim import strformat type MyError = object of Exception try: raise newException(MyError, "details about what went wrong") except MyError: echo fmt"[Error] {getCurrentException().name}: {getCurrentException().msg}" #+end_src #+RESULTS: : [Error] MyError: details about what went wrong * Rules of Thumb /(learnt from Nimisms)/ :PROPERTIES: :CUSTOM_ID: rules-of-thumb :END: /As I am collecting these rules for myself, I am seeing a trend.. *a single space can break Nim!*/ ** Don't add space before array constructors! Thanks to {{{guser(data-man)}}} for this tip. {{{nimissue(7840)}}} #+begin_src nim :eval no var foo: array [0 .. 4, bool] echo foo #+end_src Above gives: #+begin_example nim_src_6ylaig.nim(4, 5) Error: invalid type: 'T' in this context: 'array' for var #+end_example But remove that space before the ~[~ (/aka/ the array constructor), and it works! #+begin_src nim var foo: array[0 .. 4, bool] echo foo #+end_src #+RESULTS: : [false, false, false, false, false] ** Always surround = sign with spaces While below works: #+begin_src nim let foo=true echo foo #+end_src #+RESULTS: : true Below doesn't! #+begin_src nim :eval no let foo=@["abc", "def"] echo foo #+end_src You will get this error: #+begin_example int: system [Processing] Hint: nim_src_kowItE [Processing] nim_src_kowItE.nim(4, 10) Error: ':' or '=' expected, but found '[' /bin/sh: /tmp/babel-wQPYTr/nim_src_kowItE: Permission denied #+end_example But once you surround that ~=~ with spaces, it will work. So *always do that*. #+begin_src nim let foo: seq[string] = @["abc", "def"] echo foo #+end_src #+RESULTS: : @["abc", "def"] ** Always use spaces before and after =..= and =..<= :PROPERTIES: :CUSTOM_ID: space-around-dot-dot :END: {{{nimissue(6216)}}} #+begin_src nim var str = "abc" echo str[0 .. str.high] #+end_src #+RESULTS: : abc #+begin_src nim var str = "abc" echo str[0 ..< str.high] #+end_src #+RESULTS: : ab [[https://scripter.co/notes/string-fns-nim-vs-python/#notes][ref]] ** No space between proc identifier and list of args when num args >= 2 Below is a simple proc definition and invocation.. it works. #+begin_src nim proc foo(a: int, b: int) = echo a, " ", b foo(123, 456) #+end_src #+RESULTS: : 123 456 But see what happens when I add a space before the opening parenthesis in ~foo(123, 456)~: #+begin_src nim :eval no proc foo(a: int, b: int) = echo a, " ", b foo (123, 456) #+end_src #+begin_example nim_src_Uewd95.nim(6, 1) Error: type mismatch: got but expected one of: proc foo(a: int; b: int) expression: foo (123, 456) #+end_example With that space, Nim thinks that we are passing a tuple ~(123, 456)~ to ~foo~ proc! So *no space between proc identifier and list of arguments*! ** TODO Avoid using ~auto~ as proc return types From [[https://github.com/nim-lang/Nim/pull/7950#issuecomment-394433039][this comment]] by {{{guser(GULPF)}}}: #+begin_quote You could simplify the proc signature by having separate procs for float32/float64. IMO auto is a code smell and should be avoided, it just makes things harder to understand. #+end_quote Context: #+begin_src nim :eval no proc log*[X, B: SomeFloat](x: X, base: B): auto = ## Computes the logarithm ``base`` of ``x`` when B is float64 or X is float64: var r: float64 else: var r: float32 r = ln(x) / ln(base) return r #+end_src *** TODO Update this section with the canonical way to write the above function * Nimisms ** TO__BE__FIXED Picky about spaces before array constructor :PROPERTIES: :CUSTOM_ID: no-space-before-array-constructor :END: <2018-05-18 Fri> Below array declaration is from the [[https://nim-lang.org/docs/manual.html#statements-and-expressions-var-statement][manual]]: #+begin_src nim :eval no var a {.noInit.}: array [0 .. 1023, char] #+end_src gives this error ({{{nimissue(7840)}}}): #+begin_example Hint: system [Processing] Hint: nim_src_sSD4Jx [Processing] nim_src_sSD4Jx.nim(4, 5) Error: invalid type: 'T' in this context: 'array' for var #+end_example *** Workaround.. don't put any space before that ~[~ #+begin_src nim var a {.noInit.}: array[0 .. 1023, char] #+end_src #+RESULTS: ** TO__BE__FIXED Subrange definition with ~..<~ :PROPERTIES: :CUSTOM_ID: issue-6788 :END: <2018-05-17 Thu> While the rule in section [[#space-around-dot-dot]] applies in general, the same still does not work for the example in {{{nimissue(6788)}}}. #+begin_src nim :eval no const size = 6 type A = range[0 ..< size] #+end_src Trying to compile above gives this error: #+begin_example nim_src_bshJJ4.nim(5, 10) Error: range types need to be constructed with '..', '..<' is not supported #+end_example *** Workaround #+begin_src nim const size = 6 type A = range[0 .. (size-1)] #+end_src #+RESULTS: ** FIXED Echoing sequences {{{nimissue(6225)}}} *This issue has been fixed.* -- <2018-05-17 Thu> *** Now fixed #+begin_src nim let seq1 = @[1, 2, 3] echo "Integer elements: ", seq1 let seq2 = @['1', '2', '3'] echo "Char elements: ", seq2 let seq3 = @["1", "2", "3"] echo "String elements: ", seq3 #+end_src #+results: : Integer elements: @[1, 2, 3] : Char elements: @['1', '2', '3'] : String elements: @["1", "2", "3"] Equivalent code in Python (sort of, because Python does not have =char= vs =string=): #+begin_src python list1 = [1, 2, 3] print('Integer elements: {}'.format(list1)) list2 = ['1', '2', '3'] print('String elements: {}'.format(list2)) #+end_src #+results: : Integer elements: [1, 2, 3] : String elements: ['1', '2', '3'] *** Old issue {{{nimissue(6225)}}} Unable to distinguish the seq element types from =echo= outputs. #+begin_src nim :eval no let seq1 = @[1, 2, 3] echo "Integer elements: ", seq1 let seq2 = @['1', '2', '3'] echo "Char elements: ", seq2 let seq3 = @["1", "2", "3"] echo "String elements: ", seq3 #+end_src #+results: : Integer elements: @[1, 2, 3] : Char elements: @[1, 2, 3] : String elements: @[1, 2, 3] Same as above, but using =.repr=: #+begin_src nim :eval no let seq1 = @[1, 2, 3] echo "Integer elements (Repr): ", seq1.repr let seq2 = @['1', '2', '3'] echo "Char elements (Repr): ", seq2.repr let seq3 = @["1", "2", "3"] echo "String elements (Repr): ", seq3.repr #+end_src #+results: : Integer elements (Repr): 0x7f4c87d8e048[1, 2, 3] : : Char elements (Repr): 0x7f4c87d90048['1', '2', '3'] : : String elements (Repr): 0x7f4c87d8e0b8[0x7f4c87d900f8"1", 0x7f4c87d90120"2", 0x7f4c87d90148"3"] : Equivalent code in Python (sort of, because Python does not have =char= vs =string=): #+begin_src python list1 = [1, 2, 3] print('Integer elements: {}'.format(list1)) list2 = ['1', '2', '3'] print('String elements: {}'.format(list2)) #+end_src #+results: : Integer elements: [1, 2, 3] : String elements: ['1', '2', '3'] **** Improving the default =echo= proc, a new proc =debug= <2017-12-13 Wed> This =debug= proc is not needed, once {{{nimpr(6825)}}} gets merged. - [[https://github.com/nim-lang/Nim/issues/6225#issuecomment-321743831][Source]] by {{{guser(bluenote10)}}} #+begin_src nim :eval no import macros import sequtils import strutils import future proc toDebugRepr[T](x: T): string = when T is seq: result = "@[" & x.map(el => toDebugRepr(el)).join(", ") & "]" elif T is array: result = "[" & x.map(el => toDebugRepr(el)).join(", ") & "]" elif T is string: result = "\"" & x & "\"" elif T is char: result = "'" & x & "'" else: result = $x macro debug*(args: varargs[typed]): untyped = result = newCall(bindSym("echo")) for arg in args: result.add(newCall(bindSym("toDebugRepr"), arg)) debug 1, 2, 3 debug 123 debug "hello world" debug "a", "b" debug(@[1, 2, 3]) debug(@["1", "2", "3"]) debug(@['1', '2', '3']) debug([1, 2, 3]) debug(["1", "2", "3"]) debug(['1', '2', '3']) let temp_seq: seq[string] = "1,2,3".split(',', maxsplit=1) echo temp_seq debug temp_seq #+end_src #+results: #+begin_example 123 123 "hello world" "a""b" @[1, 2, 3] @["1", "2", "3"] @['1', '2', '3'] [1, 2, 3] ["1", "2", "3"] ['1', '2', '3'] @[1, 2,3] @["1", "2,3"] #+end_example ***** Python =print= for comparison #+begin_src python print(1, 2, 3) print("hello world") print("a", "b") print([1, 2, 3]) print(["1", "2", "3"]) #+end_src #+results: : 1 2 3 : hello world : a b : [1, 2, 3] : ['1', '2', '3'] ** FIXED Inconsistency in parentheses requirement after echo CLOSED: [2017-10-16 Mon 11:02] {{{nimissue(6210)}}} *This issue has been fixed.* #+begin_src nim import strutils echo "Sequences:" echo @["a", "b", "c"].join(" ") echo join(@["a", "b", "c"], " ") echo "Lists:" echo (["a", "b", "c"].join(" ")) # Works! echo join(["a", "b", "c"], " ") # Works! echo ["a", "b", "c"].join(" ") # This did not use to work, now works! -- Mon Oct 16 11:03:52 EDT 2017 - kmodi var list = ["a", "b", "c"] echo list.join(" ") # Works too! #+end_src #+results: : Sequences: : a b c : a b c : Lists: : a b c : a b c : a b c : a b c * Importing and Exporting Modules ** ~import~ The ~import~ statement can contain just the module name with the path, if needed too. - Importing a module present in the current directory. #+begin_example foo/ - foo.nim - bar.nim #+end_example #+begin_src nim :eval no # Inside foo.nim import bar #+end_src - Importing a module present in a sub-directory. #+begin_example foo/ - foo.nim extra/ - bar.nim #+end_example #+begin_src nim :eval no # Inside foo.nim import extra/bar #+end_src ** ~export~ The ~export~ statement can contain *only the module name* -- No paths ([[https://gitter.im/nim-lang/Nim?at=5b2d5c667da8cd7c8c64cabe][ref]]). - Importing and then exporting a module present in the current directory. #+begin_example foo/ - foo.nim - bar.nim #+end_example #+begin_src nim :eval no # Inside foo.nim import bar export bar #+end_src - Importing a module present in a sub-directory, and then exporting the same. #+begin_example foo/ - foo.nim extra/ - bar.nim #+end_example #+begin_src nim :eval no # Inside foo.nim import extra/bar export bar #+end_src *** Exporting identifiers (Asterisks after proc names, etc.) :PROPERTIES: :CUSTOM_ID: exporting-identifiers :END: From https://nim-lang.org/docs/tut1.html#modules: #+begin_quote A module may gain access to the symbols of another module by using the ~import~ statement. Only top-level symbols that are marked with an asterisk (~*~) are exported. #+end_quote I got curious about the ~*~ after I read code like below [[https://github.com/xmonader/nistow/blob/1f12c8eca103927f7ee52f43bc418181e9ddaab9/src/nistow.nim#L10][here]]: #+begin_src nim :eval no proc getLinkableFiles*(appPath: string, dest: string=expandTilde("~")): seq[LinkInfo] = #+end_src See [[#object-types]] for exporting objects and their individual fields. * Modules/Packages/Libraries :PROPERTIES: :CUSTOM_ID: modules :END: ** =sugar= (Nim 0.19.0+) :PROPERTIES: :CUSTOM_ID: sugar :END: This is a standard Nim library. This library was called =future= in Nim 0.18.0 and older. - https://nim-lang.org/docs/future.html #+begin_quote This module implements nice syntactic sugar based on Nim's macro system. #+end_quote *** DONE Lambdas (~=>~) CLOSED: [2018-07-03 Tue 12:12] :PROPERTIES: :CUSTOM_ID: lambda :END: Here is an example of using ~lambda~ in Emacs Lisp, that returns the 1-incremented value of the input. #+name: code__plus1_elisp #+caption: Example of ~lambda~ in Emacs Lisp #+begin_src emacs-lisp :eval no (let ((plus1 (lambda (x) (+ x 1)))) (funcall plus1 2)) #+end_src #+RESULTS: code__plus1_elisp : 3 Here's the same using the ~=>~ macro in Nim: #+name: code__int2int__1 #+caption: Example of lambda-like ~=>~ macro #+begin_src nim import sugar proc int2Int(fn: proc(inp: int): int, x: int): int = # Define the proc parameter and return types ## Wrapper proc to define the ``fn`` type, and to pass its inputs. fn(x) # Call the passed fn with input x echo int2Int(x => x + 1, 2) # Call the int2Int wrapper with the fn definition and input arg #+end_src #+RESULTS: code__int2int__1 : 3 - The ~int2Int~ wrapper as in the above example is needed because Nim needs to know the types of the ~fn~ proc parameters and its return value. - Later we call that wrapper with the ~fn~ definition ~x => x + 1~ and its input argument ~2~. Thanks to {{{guser(narimiran)}}} for [[https://gitter.im/nim-lang/Nim?at=5b3b97013572e970c17271c6][this snippet]] as an another example of using [[#lambda][~=>~]], with ~map~. #+begin_src nim import sequtils, sugar let a = [1, 2, 3, 5, 7] b = a.map(x => 2*x) echo b #+end_src #+RESULTS: : @[2, 4, 6, 10, 14] Also see [[#anonymous-procedures]]. *** DONE Syntactic sugar for proc types (~->~) CLOSED: [2018-07-03 Tue 12:49] :PROPERTIES: :CUSTOM_ID: proc-type-sugar :END: The ~->~ macro helps abbreviate proc types. - ~proc(inp: int): int~ can be replaced with ~int -> int~. - ~proc(x: int; y: float): string~ can be replaced with ~(int, float) -> string~. So the snippet in [[code__int2int__1]] can be rewritten using ~->~ as shown below: #+name: code__int2int__2 #+caption: Example of using proc type sugar: ~->~ #+begin_src nim import sugar proc int2Int(fn: int -> int, x: int): int = ## Wrapper proc to define the ``fn`` type, and to pass its inputs. fn(x) echo int2Int(x => x + 1, 2) #+end_src #+RESULTS: code__int2int__2 : 3 Here are few more examples of using ~=>~ and ~->~: #+begin_src nim import sugar proc twoInts2Int(fn: (int, int) -> int; x, y: int): int = ## Wrapper proc to define the ``fn`` type, and to pass its inputs. fn(x, y) proc twoFlts2Flt(fn: (float, float) -> float; x, y: float): float = ## Wrapper proc to define the ``fn`` type, and to pass its inputs. fn(x, y) echo twoInts2Int((x, y) => x + y, 2, 3) echo twoFlts2Flt((x, y) => x + y, 1.11, 2.22) #+end_src #+RESULTS: : 5 : 3.33 The above example can be made more concise by using [[#generics][Generics]]. /Thanks to {{{guser(narimiran)}}} for [[https://gitter.im/nim-lang/Nim?at=5b3ba64d63042f2df3530ad7][pointing out]] that the known types for the inputs need to come first in the ~twoInpOneOut~ proc signature below./ As the type of ~fn~ is unknown, it cannot be placed as the first parameter of ~twoInpOneOut~ as that would fail the auto-inference of types of ~x~ and ~y~. #+begin_src nim import sugar proc twoInpOneOut[T](x, y: T; fn: (T, T) -> T): T = ## Wrapper proc to define the ``fn`` type, and to pass its inputs. fn(x, y) echo twoInpOneOut(2, 3, (x, y) => x + y) echo twoInpOneOut(1.11, 2.22, (x, y) => x + y) echo twoInpOneOut("abc", "def", (x, y) => x & y) #+end_src #+RESULTS: : 5 : 3.33 : abcdef *** DONE List Comprehension (~[]~ and ~lc~) CLOSED: [2018-07-03 Tue 13:41] :PROPERTIES: :CUSTOM_ID: list-comprehension :END: List comprehension is implemented in this module using the ~[]~ macro. The syntax is: #+begin_example lc[ | ( <- [, <- , ..] [, ]), ] #+end_example - The whole ~lc[ .. ]~ expression returns a *sequence* of type ~seq[SEQ_TYPE]~. - ELEM1 is an element of LIST1, ELEM2 is an element of LIST2, and so on. - ~ <- ~ and onwards are optional. - LIST2 can use earlier declared element vars like ELEM1. - LIST3 can use earlier declared element vars like ELEM1 and ELEM2, and so on. - Optional COND can use one or more of the declared element vars. - RETURN_EXPR can use one or more of the declared element vars. - RETURN_EXPR is of the type SEQ_TYPE. Here's an example ([[https://stackoverflow.com/a/50899858/1219634][Ref]]) that splits a string into a sequence of characters: #+begin_src nim import strformat, typetraits, sugar let str = "nim lang" cSeq = lc[c | (c <- str), # lc[EXPR | (ELEM1 <- LIST1), char] # SEQ_TYPE] echo fmt"str of type {$str.type} = {str}" echo fmt"cSeq of type {$cSeq.type} (seq[SEQ_TYPE]) = {cSeq}" #+end_src #+RESULTS: : str of type string = nim lang : cSeq of type seq[char] (seq[SEQ_TYPE]) = @['n', 'i', 'm', ' ', 'l', 'a', 'n', 'g'] /Though, the above example can be written more concisely using [[#mapit][~mapIt~]] or ~map~ and [[#lambda][~=>~]] from ~sugar~ module/: #+name: code__map_and_lambda_sugar #+caption: Using ~map~ plus ~=>~ sugar #+begin_src nim import sequtils, sugar let str = "nim lang" cSeq = str.map(c => c) echo cSeq #+end_src #+RESULTS: code__map_and_lambda_sugar : @['n', 'i', 'm', ' ', 'l', 'a', 'n', 'g'] Other list comprehension examples: #+begin_src nim import sugar let numList = 1 .. 10 evenNums = lc[x | (x <- numList, # lc[EXPR | (ELEM1 <- LIST1, x mod 2 == 0), # COND), int] # SEQ_TYPE] echo evenNums #+end_src #+RESULTS: : @[2, 4, 6, 8, 10] The LIST2 and onwards can also use one or more of the prior declared ELEMs. #+begin_src nim import sugar const n = 15 let numList = 1 .. n rightAngleTriangleSides = lc[(x, y, z) | (x <- numList, # lc[EXPR | (ELEM1 <- LIST1, y <- x .. n, # ELEM2 <- LIST2, z <- y .. n, # ELEM3 <- LIST3, x*x + y*y == z*z), # COND), tuple[a, b, c: int]] # SEQ_TYPE] echo rightAngleTriangleSides #+end_src #+RESULTS: : @[(a: 3, b: 4, c: 5), (a: 5, b: 12, c: 13), (a: 6, b: 8, c: 10), (a: 9, b: 12, c: 15)] ** =parsetoml= This is an external library. - https://github.com/NimParsers/parsetoml *** Installation #+begin_example nimble install parsetoml #+end_example *** Example :noexport: #+begin_src nim :noweb-ref cfg-toml :eval no import parsetoml let cfg = parsetoml.parseString(""" [tmux] req_env_vars = ["STOW_PKGS_TARGET"] [tmux.set_env_vars] CFLAGS = "-fgnu89-inline -I${STOW_PKGS_TARGET}/include -I${STOW_PKGS_TARGET}/include/ncursesw" LDFLAGS = "-L${STOW_PKGS_TARGET}/lib" """) pkg = "tmux" #+end_src *** Extracting all keys of a TOML table [[https://github.com/NimParsers/parsetoml/issues/14#issuecomment-390223042][ref]] #+begin_src nim :noweb yes <> let tmux = cfg.getTable(pkg) for key, val in tmux.pairs: echo key #+end_src #+RESULTS: : req_env_vars : set_env_vars **** Older ways to do the same #+begin_src nim :noweb yes <> for key, val in cfg.pairs: if key == "tmux": for key, val in val.tableVal.pairs: echo key #+end_src #+RESULTS: : req_env_vars : set_env_vars #+begin_src nim :noweb yes <> let tmux = cfg.getValueFromFullAddr(pkg) for key, val in tmux.tableVal.pairs: echo key #+end_src #+RESULTS: : req_env_vars : set_env_vars ** =strfmt= (Yep, this is different from ~strformat~) :PROPERTIES: :CUSTOM_ID: strfmt :END: - My Fork :: [[https://github.com/kaushalmodi/strfmt][Source]] | [[https://kaushalmodi.github.io/strfmt/][Documentation]] - To install: ~nimble install https://kaushalmodi.github.io/strfmt/~ - Original :: [[https://bitbucket.org/lyro/strfmt/src/default/][Source]] | [[https://lyro.bitbucket.io/strfmt/][Documentation]] - To install: ~nimble install strfmt~ Thanks to the [[https://github.com/nim-lang/Nim/issues/4218#issuecomment-321340190][tip]] by {{{guser(Yardanico)}}}, the =strfmt= module by *lyro* allows using a string formatting syntax that's similar to [[https://docs.python.org/3.4/library/string.html#formatspec][Python's Format Specification Mini-Language (~.format()~)]]. #+begin_note Unfortunately though, the ~strfmt~ developer has [[https://bitbucket.org/lyro/strfmt/issues/11/why-not-submit-this-as-a-pr-to-nim-stdlib#comment-42085867][stopped using Nim]]. Also the ~fmt~ identifier from this package clashes with the ~fmt~ in the ~strformat~ module that got added to Nim 0.18.0, if both ~strfmt~ and ~strformat~ modules are imported in a Nim project. So from the aspect of future support, it might be better to use just the [[#strformat][~strformat~]] module. :disappointed: #+end_note Example of ~.format~ use in Python 3.7.0: #+begin_src python print('{} {}'.format(1, 2)) print('{} {}'.format('a', 'b')) #+end_src #+results: : 1 2 : a b Similar example using ~fmt~ from ~strfmt~ module: #+begin_src nim import strfmt echo "{} {}".fmt(1, 0) echo "{} {}".fmt('a', 'b') echo "{} {}".fmt("abc", "def") echo "{0} {1} {0}".fmt(1, 0) echo "{0.x} {0.y}".fmt((x: 1, y:"foo")) #+end_src #+results: : 1 0 : a b : abc def : 1 0 1 : 1 foo ** =bignum= This is an external library. - https://github.com/FedeOmoto/bignum - https://fedeomoto.github.io/bignum/ *** Installation #+begin_example nimble install https://github.com/kaushalmodi/bignum #+end_example *** ~bignum~ Example #+begin_src nim :tangle code/bernoulli.nim :mkdirp yes import strformat, bignum const upper = 61 var bn: array[upper, Rat] proc bernoulli(n: int): Rat = var A: seq[Rat] for i in 0 .. n: A.add(newRat()) for i in 0 .. A.high: discard A[i].set(1, i+1) for j in countDown(i, 1): A[j-1] = j*(A[j-1] - A[j]) return A[0] # (which is Bn) for i in 0 .. bn.high: bn[i] = bernoulli(i) if bn[i].toFloat != 0.0: echo fmt"B({i:2}) = {bn[i]:>55}" #+end_src #+RESULTS: #+begin_example B( 0) = 1 B( 1) = 1/2 B( 2) = 1/6 B( 4) = -1/30 B( 6) = 1/42 B( 8) = -1/30 B(10) = 5/66 B(12) = -691/2730 B(14) = 7/6 B(16) = -3617/510 B(18) = 43867/798 B(20) = -174611/330 B(22) = 854513/138 B(24) = -236364091/2730 B(26) = 8553103/6 B(28) = -23749461029/870 B(30) = 8615841276005/14322 B(32) = -7709321041217/510 B(34) = 2577687858367/6 B(36) = -26315271553053477373/1919190 B(38) = 2929993913841559/6 B(40) = -261082718496449122051/13530 B(42) = 1520097643918070802691/1806 B(44) = -27833269579301024235023/690 B(46) = 596451111593912163277961/282 B(48) = -5609403368997817686249127547/46410 B(50) = 495057205241079648212477525/66 B(52) = -801165718135489957347924991853/1590 B(54) = 29149963634884862421418123812691/798 B(56) = -2479392929313226753685415739663229/870 B(58) = 84483613348880041862046775994036021/354 B(60) = -1215233140483755572040304994079820246041491/56786730 #+end_example ** =typetraits= This is a standard Nim library. - https://nim-lang.org/docs/typetraits.html This module mainly defines the ~name~ proc which is used to get the string name of any type. It also defines ~$~ aliased to ~name~. #+begin_src nim import typetraits var x = 5 var y = "foo" # Using regular proc call style echo name(x.type), " x = ", x echo name(y.type), " y = ", y # Using UFCS echo x.type.name, " x = ", x echo y.type.name, " y = ", y # Using $ echo $x.type, " x = ", x echo $y.type, " y = ", y #+end_src #+RESULTS: : int x = 5 : string y = foo : int x = 5 : string y = foo : int x = 5 : string y = foo [[https://stackoverflow.com/a/28353109/1219634][Credit]] *** COMMENT Function types https://gitter.im/nim-lang/Nim?at=5b3a41587b811a6d63c8f814 #+begin_src nim import strformat, typetraits type Func1 = proc (p: seq[float]) Func1T[T] = proc (p: seq[T]) Func2[T] = proc (p: seq[T], d: T) proc f1(p: seq[float]) = discard proc f2(p: seq[float], d: float) = discard echo "f1 is Func1? ", f1 is Func1 # returns false echo "f1 is Func1T? ", f1 is Func1T # returns true echo "f2 is Func2? ", f2 is Func2 # returns true echo fmt"f1 is of type `{$f1.type}'" echo fmt"f2 is of type `{$f2.type}'" #+end_src #+RESULTS: : f1 is Func1? false : f1 is Func1T? true : f2 is Func2? true : f1 is of type `proc (p: seq[float]){.noSideEffect, gcsafe, locks: 0.}' : f2 is of type `proc (p: seq[float], d: float){.noSideEffect, gcsafe, locks: 0.}' *** TO__BE__FIXED Clash between ~type.name~ and a custom tuple field name :PROPERTIES: :CUSTOM_ID: issue-7975 :END: {{{nimissue(7975)}}} **** Using ~$~ works #+begin_src nim import strformat, typetraits type Person = tuple[name: string, age: int] let person: Person = ("Peter", 30) echo fmt"Tuple person of type {$person.type} = {person}" #+end_src #+RESULTS: : Tuple person of type Person = (name: "Peter", age: 30) **** Using ~name(VAR.type)~ works too #+begin_src nim import strformat, typetraits type Person = tuple[name: string, age: int] let person: Person = ("Peter", 30) echo fmt"Tuple person of type {name(person.type)} = {person}" #+end_src #+RESULTS: : Tuple person of type Person = (name: "Peter", age: 30) **** Using ~VAR.type.name~ FAILS #+begin_src nim :eval no import strformat, typetraits type Person = tuple[name: string, age: int] let person: Person = ("Peter", 30) echo fmt"Tuple person of type {person.type.name} = {person}" #+end_src #+begin_example Hint: typetraits [Processing] nim_src_2f8wWD.nim(7, 9) template/generic instantiation from here lib/pure/strformat.nim(260, 8) Error: type mismatch: got but expected one of: proc add(x: var string; y: string) first type mismatch at position: 2 required type: string but expression 'type(person).name' is of type: type string proc add(x: var string; y: char) first type mismatch at position: 2 required type: char but expression 'type(person).name' is of type: type string proc add(result: var string; x: int64) first type mismatch at position: 2 required type: int64 but expression 'type(person).name' is of type: type string proc add(result: var string; x: float) first type mismatch at position: 2 required type: float but expression 'type(person).name' is of type: type string proc add(x: var string; y: cstring) first type mismatch at position: 2 required type: cstring but expression 'type(person).name' is of type: type string proc add[T](x: var seq[T]; y: openArray[T]) first type mismatch at position: 1 required type: var seq[T] but expression 'fmtRes218041' is of type: string proc add[T](x: var seq[T]; y: T) first type mismatch at position: 1 required type: var seq[T] but expression 'fmtRes218041' is of type: string expression: add(fmtRes218041, type(person).name) #+end_example **** Changing the tuple field name to anything but "name" works (not practical) ~type.name~ works again if I change the tuple field name to anything but ~name~: #+begin_src nim import strformat, typetraits type Person = tuple[namex: string, age: int] let person: Person = ("Peter", 30) echo fmt"Tuple person of type {person.type.name} = {person}" #+end_src #+RESULTS: : Tuple person of type Person = (namex: "Peter", age: 30) *** TO__BE__FIXED Tuple type name gets stuck to the first printed tuple var :PROPERTIES: :CUSTOM_ID: issue-7976 :END: {{{nimissue(7976)}}} In the below examples: - ~person1~ is a tuple variable of custom type ~Person~. - ~person2~ is another tuple variable of the same type, but it is not assigned the ~Person~ type explicitly, it is directly assigned the ~tuple[name: string, age: int]~ type. In the below example, I am first printing ~person1~ type and value, and then the same for ~person2~. #+begin_src nim import strformat, typetraits type Person = tuple[name: string, age: int] let person1: Person = ("Peter", 30) person2: tuple[name: string, age: int] = (name: "Peter", age: 30) echo fmt"Tuple person1 of type {$person1.type} = {person1}" echo fmt"Tuple person2 of type {$person2.type} = {person2}" #+end_src #+RESULTS: : Tuple person1 of type Person = (name: "Peter", age: 30) : Tuple person2 of type Person = (name: "Peter", age: 30) /Above, I was expecting the ~person2~ type to be printed as ~tuple[name: string, age: int]~./ Now, below I just switch the order of printing the same tuple variables.. this time I print ~person2~ type and value first. .. and now, the type name printed for both ~person1~ and ~person2~ is ~tuple[name: string, age: int]~! #+begin_src nim import strformat, typetraits type Person = tuple[name: string, age: int] let person1: Person = ("Peter", 30) person2: tuple[name: string, age: int] = (name: "Peter", age: 30) echo fmt"Tuple person2 of type {$person2.type} = {person2}" echo fmt"Tuple person1 of type {$person1.type} = {person1}" #+end_src #+RESULTS: : Tuple person2 of type tuple[name: string, age: int] = (name: "Peter", age: 30) : Tuple person1 of type tuple[name: string, age: int] = (name: "Peter", age: 30) /Above, I was expecting the ~person1~ type to be printed as ~Person~./ ** =tables= :PROPERTIES: :CUSTOM_ID: tables :END: This is a standard Nim library. - https://nim-lang.org/docs/tables.html From [[https://github.com/nim-lang/Nim/wiki/Nim-for-Python-Programmers#dictionaries][ref]]: #+begin_src nim import tables, typetraits var a = {"hi": 1, "there": 2}.toTable echo a["hi"], " ", a.len echo "a is of type ", a.type.name assert a.hasKey("hi") for key, value in a: echo key, " " ,value #+end_src #+RESULTS: : 1 2 : a is of type Table[system.string, system.int] : hi 1 : there 2 From [[https://rosettacode.org/wiki/Associative_array/Creation#Nim][ref]]: #+begin_src nim import tables var hash = initTable[string, int]() # empty hash table hash2 = {"key1": 1, "key2": 2}.toTable # hash table with two keys hash3 = [("key1", 1), ("key2", 2)].toTable # hash table from tuple array hash4 = @[("key1", 1), ("key2", 2)].toTable # hash table from tuple seq value = hash2["key1"] hash["spam"] = 1 hash["eggs"] = 2 hash.add("foo", 3) echo "hash has ", hash.len, " elements" echo "hash has key foo? ", hash.hasKey("foo") echo "hash has key bar? ", hash.hasKey("bar") echo "iterate pairs:" # iterating over (key, value) pairs for key, value in hash: echo key, ": ", value echo "iterate keys:" # iterating over keys for key in hash.keys: echo key echo "iterate values:" # iterating over values for key in hash.values: echo key # getting values of specified keys echo """hash["spam"] = """, hash["spam"] # If you try to get value for an unset key, you will get # a KeyError exception. try: echo """hash["key_not_set_yet"] = """, hash["key_not_set_yet"] except KeyError: echo """hash["key_not_set_yet"] is not yet set.""" #+end_src #+RESULTS: #+begin_example hash has 3 elements hash has key foo? true hash has key bar? false iterate pairs: eggs: 2 foo: 3 spam: 1 iterate keys: eggs foo spam iterate values: 2 3 1 hash["spam"] = 1 hash["key_not_set_yet"] is not yet set. #+end_example ** =critbits= :PROPERTIES: :CUSTOM_ID: critbits :END: This is a standard Nim library. - https://nim-lang.org/docs/critbits.html /Crit-bit Trees/ were introduced by Adam Langley in [[https://www.imperialviolet.org/binary/critbit.pdf][this paper of his]]. This module is an implementation of that. To me, the ~CritBitTree[T]~ type defined by ~critbits~ looks like a *string*-indexed dictionary/hash/table/associative-array-like type. #+begin_note For brevity, now on I will refer to /CritBitTree/ as CBT. #+end_note In the documentation, the ~CritBitTree[T]~ type is defined as: #+begin_src nim :eval no CritBitTree[T] = object root: Node[T] count: int #+end_src where ~T~ is the type of the value held by each /CritBitTree/ "key" (maybe it's technically called a /node/?). #+begin_note As I am more familiar with hashes and dictionaries, I'll just refer to "whatever technically correct term for 'key' in CBT's" as just *key*. #+end_note *** CBT Sets CBT's can also be used in a non-dictionary/hash/table/associative-array fashion.. as just *sets* of the string keys. The type ~T~ for such CBT's is ~void~. #+begin_note Now on, I will refer to such CBT's, with ~T~ as ~void~, as *set-type* or *void-type*. The other category of CBT's will be referred to as *hash-type*. #+end_note *** Initializing CBT objects auto-initialize. #+begin_src nim import strformat, typetraits, critbits var s: CritBitTree[void] t: CritBitTree[int] echo fmt"{s.type.name} s (CBT as set) = {s}" echo fmt"{t.type.name} t (CBT as hash) = {t}" #+end_src #+RESULTS: : CritBitTree[system.void] s (CBT as set) = {} : CritBitTree[system.int] t (CBT as hash) = {:} **** FIXED ~$~ does not work for set-type CBT's CLOSED: [2018-06-07 Thu 11:53] :PROPERTIES: :CUSTOM_ID: issue-7987 :END: {{{nimissue(7987)}}} Fixed by {{{guser(data-man)}}} in {{{nimcommit(aa7348b356)}}}. ***** Older issue Due to this bug, the echoing of the set-type CBT ~s~ in above example was commented out. Uncommenting that line gave this error: #+begin_example nim_src_saJsiR.nim(8, 9) template/generic instantiation from here lib/pure/strformat.nim(268, 13) template/generic instantiation from here lib/pure/collections/critbits.nim(325, 26) template/generic instantiation from here lib/pure/collections/critbits.nim(244, 43) Error: undeclared field: 'val' #+end_example *** Keys **** Adding keys ***** For hash-type CBT's If your CBT variable ~t~ is of type ~CritBitTree[T]~, you assign a value to STRING key using ~t[STRING] = VAL~, where VAL is of type ~T~. #+begin_src nim import strformat, typetraits, critbits var t: CritBitTree[int] t["a"] = 1 echo fmt"{t.type.name} t = {t}" echo """Mutating or changing the value of t["a"] .. """ t["a"] = 2 echo fmt"{t.type.name} t = {t}" #+end_src #+RESULTS: : CritBitTree[system.int] t = {"a": 1} : Mutating or changing the value of t["a"] .. : CritBitTree[system.int] t = {"a": 2} *Only for "int" CBT's*, you can even use the [[#critbits-inc][~inc~]] to set initial values (defaults to 1) for unassigned keys. #+begin_src nim import critbits var t: CritBitTree[int] t.inc("a") t.inc("b") t.inc("c") t.inc("d", 10) echo t #+end_src #+RESULTS: : {"a": 1, "b": 1, "c": 1, "d": 10} Above code is equivalent to: #+begin_src nim import critbits var t: CritBitTree[int] t["a"] = 1 t["b"] = 1 t["c"] = 1 t["d"] = 10 echo t #+end_src #+RESULTS: : {"a": 1, "b": 1, "c": 1, "d": 10} Keys can also be added using ~incl~. #+begin_src nim import critbits var t1: CritBitTree[int] t2: CritBitTree[string] t1.incl("a", 12) t1.incl("b", 34) echo t1 t1.incl("a", 77) # Overwrite the "a" key value echo t1 t2.incl("a", "def") t2.incl("b", "xyz") echo t2 #+end_src #+RESULTS: : {"a": 12, "b": 34} : {"a": 77, "b": 34} : {"a": "def", "b": "xyz"} ****** FIXED Only the first key get initialized to 1; all others init to 0 CLOSED: [2018-06-07 Thu 12:33] :PROPERTIES: :CUSTOM_ID: issue-7990 :END: {{{nimissue(7990)}}} Thanks to {{{guser(data-man)}}} for fixing this in {{{nimcommit(12f929e582)}}}. ******* Older Issue Earlier, the above output was: #+begin_example {"a": 1, "b": 0, "c": 0, "d": 0} #+end_example ***** For set-type CBT's The /set-type/ CBT's have *only keys*.. no values. So for these, you have to use the ~incl~ proc to add the /keys/ or /set elements/. #+begin_src nim import strformat, typetraits, critbits var s: CritBitTree[void] echo fmt"Type of s: {s.type.name}" echo """Adding "a" ..""" incl(s, "a") echo """Adding "b" ..""" s.incl("b") echo fmt" s has {s.len} elements: {s}" echo """Adding "a" again ..""" s.incl("a") echo fmt" s *still* has {s.len} elements: {s}" #+end_src #+RESULTS: : Type of s: CritBitTree[system.void] : Adding "a" .. : Adding "b" .. : s has 2 elements: {"a", "b"} : Adding "a" again .. : s *still* has 2 elements: {"a", "b"} **** TODO Removing keys **** Key check Use ~contains~ or its alias ~hasKey~. #+begin_src nim import strformat, typetraits, critbits var s: CritBitTree[void] t: CritBitTree[int] s.incl("y") t["a"] = 1 echo fmt"{s.type.name} s = {s}" echo fmt"""s["x"] exists? {s.contains("x")}""" echo fmt"""s["y"] exists? {s.hasKey("y")}""" echo fmt"{t.type.name} t = {t}" echo fmt"""t["a"] exists? {t.contains("a")}""" echo fmt"""t["b"] exists? {t.hasKey("b")}""" #+end_src #+RESULTS: : CritBitTree[system.void] s = {"y"} : s["x"] exists? false : s["y"] exists? true : CritBitTree[system.int] t = {"a": 1} : t["a"] exists? true : t["b"] exists? false **** Number of keys Use ~len~. #+begin_src nim import strformat, typetraits, critbits var s: CritBitTree[void] t: CritBitTree[string] s.incl("foo") s.incl("bar") s.incl("zoo") echo fmt"{s.type.name} s = {s}" echo fmt"Number of elements in s = {s.len}" t["a"] = "hello" t["b"] = "world" echo fmt"{t.type.name} t = {t}" echo fmt"Number of elements in t = {t.len}" #+end_src #+RESULTS: : CritBitTree[system.void] s = {"bar", "foo", "zoo"} : Number of elements in s = 3 : CritBitTree[system.string] t = {"a": "hello", "b": "world"} : Number of elements in t = 2 *** Values **** Incrementing/Decrementing key values *This works only for CBT's of type ~CritBitTree[int]~.* ***** Incrementing key values :PROPERTIES: :CUSTOM_ID: critbits-inc :END: #+begin_src nim import strformat, typetraits, critbits var t: CritBitTree[int] t["a"] = 10 echo fmt"{t.type.name} t = {t}" inc(t, "a") echo fmt"{t.type.name} t = {t}" t.inc("a") echo fmt"{t.type.name} t = {t}" #+end_src #+RESULTS: : CritBitTree[system.int] t = {"a": 10} : CritBitTree[system.int] t = {"a": 11} : CritBitTree[system.int] t = {"a": 12} ***** Decrementing key values The ~inc~ proc increments the key value by 1 by default. But if a negative "incrementing" value is provided, it can decrement too. #+begin_src nim import strformat, typetraits, critbits var t: CritBitTree[int] echo fmt"{t.type.name} t = {t}" echo """Initializing/incrementing "a" key to 1 ..""" t.inc("a") echo fmt" {t.type.name} t = {t}" echo """Decrementing "a" key by 1 ..""" t.inc("a", -1) echo fmt" {t.type.name} t = {t}" echo """Incrementing "a" key by 4 ..""" t.inc("a") t.inc("a") t.inc("a") t.inc("a") echo fmt" {t.type.name} t = {t}" echo """Decrementing "a" key by 2 ..""" t.inc("a", -1) t.inc("a", -1) echo fmt" {t.type.name} t = {t}" #+end_src #+RESULTS: : CritBitTree[system.int] t = {:} : Initializing/incrementing "a" key to 1 .. : CritBitTree[system.int] t = {"a": 1} : Decrementing "a" key by 1 .. : CritBitTree[system.int] t = {"a": 0} : Incrementing "a" key by 4 .. : CritBitTree[system.int] t = {"a": 4} : Decrementing "a" key by 2 .. : CritBitTree[system.int] t = {"a": 2} Extending the above, you can increment/decrement by any value other than 1 too. #+begin_src nim import strformat, typetraits, critbits var t: CritBitTree[int] echo fmt"{t.type.name} t = {t}" t.inc("a", 5) t.inc("b", 7) echo fmt"{t.type.name} t = {t}" t.inc("a", -10) t.inc("b", -100) echo fmt"{t.type.name} t = {t}" #+end_src #+RESULTS: : CritBitTree[system.int] t = {:} : CritBitTree[system.int] t = {"a": 5, "b": 7} : CritBitTree[system.int] t = {"a": -5, "b": -93} **** Retrieving key values (only for /hash-type/ CBT's) *This works only for /hash-type/ CBT's.* *If you do know* that an X key is present in a CBT ~t~, you can get that key's value using ~t[X]~. #+begin_src nim import strformat, typetraits, critbits var t: CritBitTree[int] t["a"] = 1 echo fmt"""t["a"] = {t["a"]}""" #+end_src #+RESULTS: : t["a"] = 1 Trying to do ~t[X]~ for a /set-type/ CBT will give this error: #+begin_example nim_src_LF3H1K.nim(9, 9) template/generic instantiation from here lib/pure/strformat.nim(313, 39) template/generic instantiation from here lib/pure/collections/critbits.nim(203, 6) Error: undeclared field: 'val' #+end_example *If you don't know* whether an X key is present in a CBT ~t~, you need to first check if that key exists using [[* Key check][~hasKey~]]. #+begin_src nim import critbits var t: CritBitTree[int] t["a"] = 123 if t.hasKey("a"): echo """t["a"] = """, t["a"] if t.hasKey("b"): echo """t["b"] = """, t["b"] # this will not be printed #+end_src #+RESULTS: : t["a"] = 123 *** TODO Iterators ** TODO =sequtils= :PROPERTIES: :CUSTOM_ID: sequtils :END: This is a standard Nim library. - https://nim-lang.org/docs/sequtils.html *** ~mapIt~ :PROPERTIES: :CUSTOM_ID: mapit :END: [[https://nim-lang.org/docs/sequtils.html#mapIt.t,untyped,untyped][~mapIt~]] is a convenience template around the ~map~ proc to reduce typing. The template injects the *it* variable which you can use directly in an expression. #+begin_src nim import sequtils let nums = @[1, 2, 3, 4] strings = nums.mapIt($(4 * it)) echo strings #+end_src #+RESULTS: : @["4", "8", "12", "16"] Below shows the how the [[code__map_and_lambda_sugar][~map~ /plus/ ~=>~]] can be rewritten a bit more concisely using ~mapIt~ (/Thanks to the SO user Peheje for [[https://stackoverflow.com/questions/50853426/how-do-i-convert-a-string-into-a-sequence-of-characters-in-nim/51160075#comment90563369_51160075][this tip]]/): #+begin_src nim import sequtils let str = "nim lang" cSeq = str.mapIt(it) echo cSeq #+end_src #+RESULTS: : @['n', 'i', 'm', ' ', 'l', 'a', 'n', 'g'] ** DONE =options= CLOSED: [2018-10-08 Mon 11:52] :PROPERTIES: :CUSTOM_ID: options :END: This is a standard Nim library. - https://nim-lang.org/docs/options.html *** Declaring ~Option~ variables #+begin_src nim import options var s: Option[string] echo s #+end_src #+RESULTS: : None[string] An unset /Option/ variable defaults to ~None[TYPE]~. *** Setting ~Option~ variables #+begin_src nim import options var s: Option[float] s = some(123.0) echo s # s = some(1) # Above will give this error: # Error: type mismatch: got but expected 'Option[system.float]' # So specify the type explicitly. s = some[float](1) echo s s = none(float) echo s s = none[float]() # This is the same as none(string) used above. echo s #+end_src #+RESULTS: : Some(123.0) : Some(1.0) : None[float] : None[float] *** Getting ~Option~ variable values #+begin_src nim import options var s: Option[int] echo s s = some(456) echo s echo s.get() #+end_src #+RESULTS: : None[int] : Some(456) : 456 *** Checking if ~Option~ is /None/Some/ #+begin_src nim import options, strformat var s: Option[string] echo s echo fmt"Is s None? {s.isNone()}, is s Some? {s.isSome()}" s = some("") echo s echo fmt"Is s None? {s.isNone()}, is s Some? {s.isSome()}" s = some("abc") echo s echo fmt"Is s None? {s.isNone()}, is s Some? {s.isSome()}" #+end_src #+RESULTS: : None[string] : Is s None? true, is s Some? false : Some("") : Is s None? false, is s Some? true : Some("abc") : Is s None? false, is s Some? true *** ~Option[string]~ as a solution to "nil" string :PROPERTIES: :CUSTOM_ID: nil-string :END: In Nim 0.19.0+, ~string~ type vars cannot be set to /nil/. Earlier a string was set to nil to distinguish between "string not set" and "string set to an empty string". So to solve that problem, one possible solution is to use the ~Option[string]~ type. - An unset ~Option[string]~ would be ~None[string]~. - An ~Option[string]~ set to an empty string (~some("")~) would be ~Some("")~. #+begin_src nim import options var s: Option[string] echo s s = some("") echo s s = some("abc") echo s for c in get(s): # Get the value of type T from an Option[T] type var echo c #+end_src #+RESULTS: : None[string] : Some("") : Some("abc") : a : b : c **** FIXED ~$~ for ~Option[string]~ should wrap the output in double quotes CLOSED: [2018-08-23 Thu 14:19] {{{nimissue(8658)}}} ** TODO =json= This is a standard Nim library. - [[https://nim-lang.org/docs/json.html]] *** Looping through keys in JSON [[https://forum.nim-lang.org/t/3985][Ref]] #+begin_src nim import json var js = parseJson""" [{"mango":"green"} , {"orange":"yellow"} , {"peach":"red"} , {"grape":"black"}]""" var keys = newSeq[string]() for obj in js: for k, _ in obj: keys.add k echo keys #+end_src #+RESULTS: : @["mango", "orange", "peach", "grape"] *** Getting JSON via an API endpoint - When using the ~httpclient~ module to get content from an /https:/ domain, compile the code with ~-d:ssl~ switch. #+begin_src nim :flags -d:ssl import httpclient, json var client = newHttpClient() let jObj = client.getContent("https://scripter.co/jf2feed.json").parseJson() echo jObj["author"] echo jObj["author"].pretty() echo jObj["author"]["name"] #+end_src #+RESULTS: : {"name":"Kaushal Modi","type":"card","url":"https://scripter.co/"} : { : "name": "Kaushal Modi", : "type": "card", : "url": "https://scripter.co/" : } : "Kaushal Modi" [[https://gitter.im/nim-lang/Nim?at=5b85b35260f9ee7aa4a50361][ref]] *** Empty JSON #+begin_src nim import json echo "[]".parseJson #+end_src #+RESULTS: : [] * Nim By Example Examples in this section are from https://nim-by-example.github.io/. ** DONE Hello World CLOSED: [2018-05-24 Thu 12:44] See section [[* Echo]]. ** DONE Variables CLOSED: [2018-05-24 Thu 13:22] See section [[* Variables]]. *** DONE Result CLOSED: [2018-05-24 Thu 12:47] See section [[#return-and-result]]. *** DONE Type Casting and Inference CLOSED: [2018-05-24 Thu 13:21] Nim is a statically typed language. As such, each variable has a type associated with it. **** Inferred Types As seen in the previous example these types are inferred in the const, let and var declarations by the compiler. #+begin_src nim # These types are inferred. var x = 5 # int var y = "foo" # string import typetraits echo "x is ", x.type.name echo "y is ", y.type.name #+end_src #+RESULTS: : x is int : y is string Assigning a value of a different type will result in a compile-time error. #+begin_src nim :eval no var x = 5 # int var y = "foo" # string x = y #+end_src #+begin_example nim_src_PgW593.nim(7, 3) Error: type mismatch: got but expected 'int' #+end_example **** Converting Types Type *conversion* can be done by calling the /type/ as a ~proc~. #+begin_src nim import typetraits, strformat var x = 1.0 xType = x.type.name echo fmt"x is {xType} and its value is {x}." var xCasted = x.int # int(x) will also work xCastedType = xCasted.type.name echo fmt"xCasted is {xCastedType} and its value is {xCasted}." #+end_src #+RESULTS: : x is float64 and its value is 1.0. : xCasted is int and its value is 1. **** Casting Types Casting is done using the ~cast~ keyword (/which is not recommended/). See section [[* Specifying Types]] for an example. **** Specifying Types You may optionally specify the type after a colon (:). In some cases the compiler will expect you to explicitly cast types, for which multiple ways are available: - type conversion, whose safety checked by the compiler #+begin_src nim import typetraits, strformat var x = int(1.0 / 3) # type conversion echo fmt"x is {x.type.name} and its value is {x}." #+end_src #+RESULTS: : x is int and its value is 0. - annotating the variable type. In the below example, ~y~ is an empty seq and it needs a type specification. #+begin_src nim import typetraits, strformat var y: seq[int] = @[] # empty seq needs type specification echo fmt"y is {y.type.name} and its value is {y}." #+end_src #+RESULTS: : y is seq[int] and its value is @[]. Failing to specify the type for empty sequences will give an error like: #+begin_example nim_src_ofNFSG.nim(5, 9) Error: cannot infer the type of the sequence #+end_example - the ~cast~ keyword, which is unsafe and should be used only where you know what you are doing, such as in interfacing with C #+begin_src nim var z = "Foobar" proc ffi(foo: ptr array[6, char]) = echo repr(foo) let zAddr = addr z[0] zAddrCastedToPtr = cast[ptr array[6, char]](zAddr) ffi(zAddrCastedToPtr) #+end_src #+RESULTS: : ref 0x7fa8b40bc058 --> ['F', 'o', 'o', 'b', 'a', 'r'] : ** DONE If, Else, While CLOSED: [2018-05-24 Thu 16:08] :PROPERTIES: :CUSTOM_ID: is-else-while :END: Nim has many different control flow constructs, including the standard ~if~, ~else~, and ~while~. For "else if", Nim uses ~elif~. - When inside a loop, ~continue~ can be used to skip the rest of the loop body. - To begin the next iteration, ~break~ can be used to immediately leave the loop body. #+begin_src nim :eval no import strutils, random randomize() let answer = rand(9) + 1 # random value in the range [1,10] while true: echo "I have a number from 1 to 10, what is it? " let guess = parseInt(stdin.readLine) if guess < answer: echo "Too low, try again" elif guess > answer: echo "Too high, try again" else: echo "Correct!" break #+end_src *** Labeled ~block~ statement :PROPERTIES: :CUSTOM_ID: labeled-block :END: Along with its other uses, the ~block~ statement can be used to create a label so that it's possible to break out of nested loops. #+begin_src nim echo "I am outside" block busyloops: echo " I am in the busyloops block" while true: echo " I am in the first while" while true: echo " I am in the second while" break busyloops echo " I am in the first while again" echo " I am in the outside the first while" echo "I am outside again" #+end_src #+RESULTS: : I am outside : I am in the busyloops block : I am in the first while : I am in the second while : I am outside again If we wanted to use just ~break~ instead of ~break BLOCK_LABEL~, we would have need to do this: #+begin_src nim echo "I am outside" block busyloops: echo " I am in the busyloops block" while true: echo " I am in the first while" while true: echo " I am in the second while" break echo " I am in the first while again" break echo " I am in the outside the first while" echo "I am outside again" #+end_src #+RESULTS: : I am outside : I am in the busyloops block : I am in the first while : I am in the second while : I am in the first while again : I am in the outside the first while : I am outside again ** DONE Case Statements CLOSED: [2018-05-25 Fri 14:29] Nim also supports case statements, which are like switches in other languages. - You can use strings in the switch statement. #+begin_src nim proc abc(str: string) = case str of "alfa": echo "A" of "bravo": echo "B" of "charlie": echo "C" else: echo "Unrecognized letter" abc("alfa") abc("bravo") abc("charlie") abc("delta") #+end_src #+RESULTS: : A : B : C : Unrecognized letter - Sets and ranges of ordinal types are also usable. #+begin_src nim proc vowel_consonant(c: char) = case c of 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U': echo "Vowel" of 'b' .. 'd', 'f' .. 'h', 'j' .. 'n', 'p' .. 't', 'v' .. 'z', 'B' .. 'D', 'F' .. 'H', 'J' .. 'N', 'P' .. 'T', 'V' .. 'Z': echo "Consonant" else: echo "Unknown" vowel_consonant('A') vowel_consonant('c') vowel_consonant('^') #+end_src #+RESULTS: : Vowel : Consonant : Unknown - ~case~ statements, like most things, are actually expressions. #+begin_src nim proc positiveOrNegative(num: int): string = result = case num of low(int) .. -1: "negative" of 0: "zero" of 1 .. high(int): "positive" else: "impossible" echo positiveOrNegative(-1) echo positiveOrNegative(10000000) echo positiveOrNegative(0) #+end_src #+RESULTS: : negative : positive : zero - It is required that the ~of~ statements cover *every possible value* for the case expression. If the ~of~ statements fail to do this, you need to include the ~else:~ condition as an umbrella to cover all those remaining cases. Compiling below will give an error: #+begin_src nim :eval no var b = true case b of true: echo "true" #+end_src #+begin_example nim_src_cCE560.nim(5, 1) Error: not all cases are covered #+end_example But the below works (~of~ statements cover all cases): #+begin_src nim var b = true case b of true: echo "true" of false: echo "false" #+end_src #+RESULTS: : true And so does this (using ~else~ to cover remaining cases if any): #+begin_src nim var b = true case b of true: echo "true" else: echo "false" #+end_src #+RESULTS: : true Below is another example where the ~else~ is not needed, because the case statement covers all the possible values of ~f~ (just ~fooBar~ in this example). #+begin_src nim type Foo = enum fooBar let f: Foo = fooBar case f of fooBar: echo "fooBar" #+end_src #+RESULTS: : fooBar *** Loose ~case~ syntax For historic reasons, the colon (~:~) at the end of the ~case~ statement is optional, and requirement to indent the following ~of~ statements is relaxed too. So while all of the below styles compile, as per {{{guser(dom96)}}}, the first style below is the "one-true-way" ([[https://gitter.im/nim-lang/Nim?at=5b1afdb814d4bc146e76bde1][ref]]). - No colon, no indented ~of~ #+begin_src nim case true of true: echo "yes" else: echo "no" #+end_src #+RESULTS: : yes - With colon, with indented ~of~ /Personally I like this style as it is consistent with ~if~, ~while~, etc./ But I will stick with the above /officially preferred/ style everywhere for *consistency*. #+begin_src nim case true: of true: echo "yes" else: echo "no" #+end_src #+RESULTS: : yes - No colon, with indented ~of~ #+begin_src nim case false of true: echo "yes" else: echo "no" #+end_src #+RESULTS: : no - With colon, no indented ~of~ #+begin_src nim case false: of true: echo "yes" else: echo "no" #+end_src #+RESULTS: : no ** DONE For Loops & Iterators CLOSED: [2018-05-30 Wed 14:23] Nim has first class iterators and syntax to use them --- ~for~ loops. The ~continue~ and ~break~ keywords also work inside ~for~ loops. *** ~items~ and ~pairs~ There are two kinds of iterator, and two special methods that ~for~ loops work with --- ~items~ and ~pairs~. When iterating over an object with one item, Nim will call an iterator called ~items~ with the first parameter the type you want to iterate over. The same thing happens when iterating with two items, but in that case, the ~pairs~ iterator is called. #+begin_src nim type AlphaRange = object low: int high: int let foo = AlphaRange(low: 7, high: 11) # Define single value yielding iterator for type AlphaRange iterator items(ar: AlphaRange): int = var i = ar.low while i <= ar.high: yield i inc i for n in foo: # uses AlphaRange.items echo n # Define pair value yielding iterator for type AlphaRange iterator pairs(ar: AlphaRange): (int, char) = for i in ar: # uses AlphaRange.items yield (i, char(i + ord('a') - 1)) for i, c in foo: # uses AlphaRange.pairs echo i, " ", c #+end_src #+RESULTS: #+begin_example 7 8 9 10 11 7 g 8 h 9 i 10 j 11 k #+end_example #+begin_note A ~pairs~ iterator returns a 2-field /tuple/. #+end_note *** Operators :PROPERTIES: :CUSTOM_ID: iterators-operators :END: Iterators can also be operators in the [[#procs-operators][standard way]], with the *name enclosed in backticks*. For example, the standard library defines the [[#slice-iterators][slice iterator ~..~]], which allows iterating through [[#ordinals][ordinal types]]. Below, the same iterator is mimicked using a different name ~...~ (one more dot) to avoid conflict. #+begin_note Always use a space before and after the iterator operators! See section [[#space-around-dot-dot]]. #+end_note #+begin_src nim iterator `...`[T](a: T, b: T): T = var res: T = a # var res: T = T(a) # same as above, but a is explicitly type-casted with T. while res <= b: yield res inc res for i in 0 ... 5: echo i #+end_src #+RESULTS: : 0 : 1 : 2 : 3 : 4 : 5 Just for fun, below an iterator with ~.++~ operator is created that increments each output by 2. #+begin_src nim iterator `.++`[T](a: T, b: T): T = ## Increment each output by 2 var res: T = a while res <= b: yield res inc res inc res for i in 0 .++ 8: echo i #+end_src #+RESULTS: : 0 : 2 : 4 : 6 : 8 **** Slice iterators ~..~ and ~..<~ :PROPERTIES: :CUSTOM_ID: slice-iterators :END: The ~..~ iterator includes the ending value in its output. So iterating through ~0 .. 3~ will iterate through 0 → 1 → 2 → 3. /Think of range ~[a,b]~/. #+begin_src nim for i in 0 .. 3: echo i #+end_src #+RESULTS: : 0 : 1 : 2 : 3 The ~..<~ iterator does not include that ending value. So iterating through ~0 ..< 3~ will iterate through 0 → 1 → 2. /Think of range ~[a,b)~/. #+begin_src nim for i in 0 ..< 3: echo i #+end_src #+RESULTS: : 0 : 1 : 2 *** Inline Iterators Inline iterators basically take the body of the for loop and inline it into the iterator. This means that they do not have any overhead from function calling, but if carelessly created may increase code size dramatically. #+begin_src nim iterator countTo(n: int): int = var i = 0 while i <= n: yield i inc i for i in countTo(5): echo i #+end_src #+RESULTS: : 0 : 1 : 2 : 3 : 4 : 5 *** Closure Iterators /Closure iterators/ are procs that return iterators. #+name: code__closure_iterator_countTo #+caption: ~countTo~ closure iterator defined with explicit return type #+begin_src nim :noweb-ref closure_iterator_countTo :eval no proc countTo(n: int): iterator(): int = ## Returns an iterator with return type int return iterator(): int = var i = 0 while i <= n: yield i inc i #+end_src - Closure iterators hold on to their state and can be resumed at any time. - The ~finished()~ function can be used to check if there are any more elements available in the iterator. - However, raw iterator usage is unintuitive and difficult to get right. #+begin_src nim :noweb yes <> let countTo20 = countTo(20) echo countTo20() echo countTo20() var output = "" # Raw iterator usage: while true: # 1. grab an element let next = countTo20() # 2. If the iterator doesn't have any more elements, break out of # this while loop. if finished(countTo20): break # 3. Loop body goes here: output.add($next & " ") # "$next" stringifies "next" echo output #+end_src #+RESULTS: : 0 : 1 : 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Here the same ~countTo~ closure iterator is used to generate an iterator with a different max value. #+begin_src nim :noweb yes <> var output = "" let countTo9 = countTo(9) for i in countTo9(): output.add($i) echo output #+end_src #+RESULTS: : 0123456789 **** Closure Iterator: ~auto~ return type As seen in [[code__closure_iterator_countTo]], the return /type/ of that closure iterator is ~iterator(): int~. But that return type is already defined in that closure in the ~return iterator(): int =~ line. The Nim [[https://nim-lang.org/docs/manual.html#types-auto-type][~auto~ type]] can be used to help to prevent that repetition. Here's the same snippet rewritten using ~auto~ return type: #+name: code__closure_iterator_countTo_auto #+caption: ~countTo~ closure iterator defined with ~auto~ return type #+begin_src nim import strformat, typetraits proc countTo(n: int): auto = ## Returns an iterator with return type int return iterator(): int = var i = 0 while i <= n: yield i inc i echo fmt"countTo(10) is of type `{$countTo(10).type}'" #+end_src #+RESULTS: code__closure_iterator_countTo_auto : countTo(10) is of type `iterator (): int{.closure, noSideEffect, gcsafe, locks: 0.}' *** Counting up and down For counting up by increments of 1, you would normally use, what I call, the /dot-dot/ iterator. #+begin_src nim for i in 0 .. 3: echo i #+end_src #+RESULTS: : 0 : 1 : 2 : 3 **** ~countUp~ iterator The same thing can be done using the [[https://nim-lang.org/docs/system.html#countup.i,T,T,int][~countUp~ iterator]] too: #+begin_src nim for i in countUp(0, 3): echo i #+end_src #+RESULTS: : 0 : 1 : 2 : 3 This iterator, though, also allows setting the increment step (which defaults to 1): #+begin_src nim for i in countUp(0, 3, 2): echo i #+end_src #+RESULTS: : 0 : 2 **** ~countDown~ iterator In the similar vein, there's a [[https://nim-lang.org/docs/system.html#countdown.i,T,T,int][~countDown~ iterator]] for counting down too, which also has a decrement step parameter that defaults to 1. #+begin_src nim for i in countDown(3, 0): echo i echo "" for i in countDown(3, 0, 2): echo i #+end_src #+RESULTS: : 3 : 2 : 1 : 0 : : 3 : 1 ** DONE Procs CLOSED: [2018-06-04 Mon 13:51] Procedures in Nim are declared using ~proc~ and require their parameter and return types be annotated. Though, that type annotation is not needed if the parameters are assigned default values in the proc signature itself. After the types and parameters, an ~=~ is used to denote the start of the function body. Some terminology --- For a proc with a signature ~proc foo(a: int) = {..}~, and its call ~foo(100)~, ~a~ is called a *parameter*, and 100 is an *argument* to ~foo~. /(Ref: [[https://nim-lang.org/docs/tut1.html#procedures][Nim Tutorial -- Procedures]])/ *** Semicolons and type propagation Semicolons are used in a procedure's signature to separate and group parameters of the same type together. This also provides visual distinction as to where a parameter type changes. Below ~foo1~ and ~foo2~ are functionally the exact same -- the ~int~ type propagates to ~b~ and then ~a~, and the ~bool~ type propagates to ~d~ and then ~c~. #+begin_src nim # Using only commas proc foo1(a, b: int, c, d: bool) = echo a, b, c, d foo1(1, 2, true, false) # Using semicolon for visual distinction proc foo2(a, b: int; c, d: bool) = echo a, b, c, d foo2(1, 2, true, false) #+end_src #+RESULTS: : 12truefalse : 12truefalse But incorrect use of semicolons will result in error. Below will leave the ~a~ parameter untyped as that ";" stopped the "int" type propagation. #+begin_src nim :eval no proc foo3(a; b: int; c, d: bool) = echo a, b, c, d foo3(1, 2, true, false) #+end_src #+begin_example nim_src_KzhwQV.nim(4, 11) Error: typeless parameters are obsolete #+end_example See [[https://nim-lang.org/docs/manual.html#procedures][Procedures]] for more. *** UFCS / Command Invocation Syntax :PROPERTIES: :CUSTOM_ID: ufcs :END: Nim procedures have [[https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax][Uniform Function Call Syntax (UFCS)]], which means that they can be called using either ~foo(a, b)~ or ~a.foo(b)~. #+begin_src nim :eval no proc fibonacci(n: int): int = if n < 2: result = n else: result = fibonacci(n - 1) + (n - 2).fibonacci #+end_src See also [[https://nim-lang.org/docs/manual.html#procedures-command-invocation-syntax][Command Invocation Syntax]]. *** Exporting Symbols [[https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)][Encapsulation]] (/A language mechanism for restricting direct access to some of the object's components./) is also supported, by annotating a procedure with ~*~, which exports it and makes it available for use by modules. If you have a *module1.nim* as below: #+begin_src nim :tangle "code/exporting_symbols/module1.nim" :mkdirp yes :eval no proc foo*(): int = 2 proc bar(): int = 3 #+end_src And then if you have a *module2.nim* (in the same directory as *module1.nim*) as below: #+begin_src nim :tangle "code/exporting_symbols/module2.nim" :mkdirp yes :eval no import module1 echo foo() # Valid echo bar() # will not compile #+end_src it will fail compilation with this error: #+begin_example module2.nim(3, 6) Error: undeclared identifier: 'bar' #+end_example That's because ~foo~ got exported from *module1* because of ~foo*~, but ~bar~ didn't (because of the lack of ~*~). **** COMMENT Testing the above 2 snippets 1. Place the cursor in each of the above 2 src blocks and run ~C-c C-v t~ to tangle those blocks. That will create ~module1.nim~ and ~module2.nim~ in ~./code/exporting_symbols/~. 2. Now cd to ~./code/exporting_symbols/~ and run: #+begin_example nim c module2 #+end_example 3. You will get: #+begin_example Hint: module2 [Processing] Hint: module1 [Processing] module1.nim(2, 6) Hint: 'module1.bar()[declared in module1.nim(2, 5)]' is declared but not used [XDeclaredButNotUsed] module2.nim(3, 6) Error: undeclared identifier: 'bar' #+end_example *** Side effect analysis Nim provides support for functional programming and so includes the ~{.noSideEffect.}~ pragma, which statically ensures there are no side effects. Below will compile fine as it has no side effects: #+name: code__proc_sum_nosideeffect #+caption: Proc with no side effect #+begin_src nim proc sum(x, y: int): int {. noSideEffect .} = x + y #+end_src #+RESULTS: code__proc_sum_nosideeffect But below will fail: #+name: code__proc_minus_nosideeffect #+caption: Cannot use ~echo~ in proc with no side effect #+begin_src nim :eval no proc minus(x, y: int): int {. noSideEffect .} = echo x, " ", y x - y discard minus(4, 2) #+end_src with the error: #+begin_example nim_src_8RNhaJ.nim(4, 6) Error: 'minus' can have side effects #+end_example **** DONE Func CLOSED: [2018-07-16 Mon 22:26] The ~func~ keyword is a shortcut for a ~proc~ with ~{. noSideEffect .}~. So the snippet in [[code__proc_sum_nosideeffect]] can be rewritten as: #+name: code__func_sum #+caption: Func implies a proc with no side effect #+begin_src nim func sum(x, y: int): int = x + y #+end_src #+RESULTS: code__func_sum Similarly, rewriting [[code__proc_minus_nosideeffect]] with ~func~ will give the same error: #+name: code__func_minus #+caption: Cannot use ~echo~ in func #+begin_src nim :eval no func minus(x, y: int): int = echo x, " ", y x - y discard minus(4, 2) #+end_src **** No side effect debug :PROPERTIES: :CUSTOM_ID: debugecho :END: We see in snippets [[code__proc_minus_nosideeffect]] and [[code__func_minus]] that we cannot use ~echo~ as side effects are not allowed in those. But there is a way to still use /echo/ statements in no-side-effect procs (aka /funcs/) -- [[https://nim-lang.org/docs/system.html#debugEcho,varargs%5Btyped,%5D][~debugEcho~]]. #+begin_quote Same as ~echo~, but as a special semantic rule, ~debugEcho~ pretends to be free of side effects, so that it can be used for debugging routines marked as *noSideEffect*. #+end_quote So by simply replacing ~echo~ with ~debugEcho~, snippet [[code__func_minus]] will compile (and so will [[code__proc_minus_nosideeffect]]): #+name: code__func_minus_debugecho #+caption: Using ~debugEcho~ in func #+begin_src nim func minus(x, y: int): int = debugEcho x, " ", y x - y discard minus(4, 2) #+end_src #+RESULTS: code__func_minus_debugecho : 4 2 *** Operators :PROPERTIES: :CUSTOM_ID: procs-operators :END: To create an operator, the symbols that are to be used must be encased inside backquotes (~`~) to signify that they are *operators*. #+begin_src nim proc `$`(twoDArray: array[2, array[2, int]]): string = result = "" for arr_elem in twoDArray: for elem in arr_elem: result.add($elem & ", ") result.add("\n") echo([[1, 2], [3, 4]]) #+end_src #+RESULTS: : 1, 2, : 3, 4, : See [[* Varargs]] for how ~echo~ works in the above snippet. And you even go crazy with operator names.. #+begin_src nim proc `^&*^@%`(a, b: string): string = ## A confusingly named useless operator result = a[0] & b[high(b)] echo "foo" ^&*^@% "bar" #+end_src #+RESULTS: : fr *** Generic Functions (Generics) :PROPERTIES: :CUSTOM_ID: generics :END: From [[https://nim-lang.org/docs/tut2.html#generics][Nim Tutorial 2]]: #+begin_quote Generics are Nim's means to parametrize procs, iterators or types with type parameters. They are most useful for efficient type safe containers. #+end_quote #+begin_note I think of generics as [[http://www.chipverify.com/system-verilog/parameterized-classes][parameterized classes]] in SystemVerilog. #+end_note *Below is a badly implemented example.* While ~"a" * 10~ works, ~10 * "a"~ will fail compilation. But the example gives a good idea of what a generic function looks like. #+begin_src nim proc `+`(a, b: string): string = a & b proc `*`[T](a: T, b: int): T = result = "" for i in 0 .. b-1: result = result + a # calls `+` proc for strings defined above echo("a" * 10) # This works. # echo(10 * "a") # But this won't. #+end_src #+RESULTS: : aaaaaaaaaa ** DONE First Class Functions CLOSED: [2018-06-04 Mon 14:04] :PROPERTIES: :CUSTOM_ID: first-class-functions :END: From [[https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function][MDN web docs]]: #+begin_quote A programming language is said to have *First-class functions* when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable. #+end_quote #+begin_src nim import sequtils # needed for filter let powersOfTwo = @[1, 2, 4, 8, 16, 32] proc greaterThan4(x: int): bool = x > 4 echo powersOfTwo.filter(greaterThan4) #+end_src #+RESULTS: : @[8, 16, 32] *** Closures From [[https://nim-lang.org/docs/manual.html#procedures-closures][Closures]]: #+begin_quote Procedures can appear at the top level in a module as well as inside other scopes, in which case they are called *nested procs*. A nested proc can access local variables from its enclosing scope and if it does so it becomes a *closure*. Any captured variables are stored in a hidden additional argument to the closure (its environment) and they are accessed by reference by both the closure and its enclosing scope (i.e. any modifications made to them are visible in both places). The closure environment may be allocated on the heap or on the stack if the compiler determines that this would be safe. #+end_quote Two different syntaxes are available for /closures/: 1. Anonymous procedures 2. Do notation **** Anonymous procedures :PROPERTIES: :CUSTOM_ID: anonymous-procedures :END: These use the ~proc~ syntax identical to regular procedure syntax, but without the procedure name. #+begin_note I think of /anonymous procedures/ as the bare /lambdas/ in Emacs Lisp. #+begin_src emacs-lisp :exports both :results verbatim :eval no (cl-remove-if-not (lambda (x) (> x 4)) '(1 2 4 8 16 32)) #+end_src #+RESULTS: : (8 16 32) #+end_note #+caption: Anonymous Procedures #+name: code__anonymous_proc #+begin_src nim import sequtils # needed for filter let powersOfTwo = @[1, 2, 4, 8, 16, 32] # Using procname(arg1, arg2) syntax echo filter(powersOfTwo, proc (x: int): bool = x > 4) # Using Command Invocation Syntax: arg1.procname(arg2) echo powersOfTwo.filter(proc (x: int): bool = x > 4) #+end_src #+RESULTS: code__anonymous_proc : @[8, 16, 32] : @[8, 16, 32] /Also see [[#lambda]]./ **** Do notation :PROPERTIES: :CUSTOM_ID: do-notation :END: - Ref :: [[https://nim-lang.org/docs/manual.html#procedures-do-notation][Do notation]] from manual As a special more convenient notation, proc expressions involved in procedure calls can use the ~do~ keyword. Below example is a re-written version of snippet [[code__anonymous_proc]] using the ~do~ notation: #+begin_src nim import sequtils # needed for filter let powersOfTwo = @[1, 2, 4, 8, 16, 32] echo(powersOfTwo.filter do (x: int) -> bool: x > 4) # Below does the same thing as above. echo(powersOfTwo.filter do (x: int) -> bool: x > 4) #+end_src #+RESULTS: : @[8, 16, 32] : @[8, 16, 32] Below example is from Nim by Examples, but with a different value for ~cities~. It sorts the city names from shortest names to the longest. #+begin_src nim import algorithm # for sort var cities = @["Frankfurt", "Ahmedabad", "Tokyo", "New York", "Kyiv", "Diu"] sort(cities) do (x, y: string) -> int: cmp(x.len, y.len) echo cities #+end_src #+RESULTS: : @["Diu", "Kyiv", "Tokyo", "New York", "Frankfurt", "Ahmedabad"] (See the documentation of ~sort~ in [[https://nim-lang.org/docs/algorithm.html][~algorithm~]] module for more info.) While it works, I noticed that the sorting didn't work like I wanted when the string lengths were the same.. I expected "Ahmedabad" to come before "Frankfurt". So here's a slight tweak to the above sort algorithm to fix that: #+begin_src nim import algorithm # for sort var cities = @["Frankfurt", "Ahmedabad", "Tokyo", "New York", "Kyiv", "Diu"] sort(cities) do (x, y: string) -> int: result = cmp(x.len, y.len) if result == 0 and x.len > 0: result = cmp(x[0], y[0]) echo cities #+end_src #+RESULTS: : @["Diu", "Kyiv", "Tokyo", "New York", "Ahmedabad", "Frankfurt"] Below example from the ~sort~ documentation sorts the names by /surnames/ first. If the /surnames/ match exactly, they are then sorted by /names/. #+begin_src nim import algorithm # for sort type Person = tuple[name: string, surname: string] var people: seq[Person] = @[("Mike", "Smith"), ("Dave", "Smith"), ("Thomas", "Edison")] people.sort do (x, y: Person) -> int: result = cmp(x.surname, y.surname) if result == 0: result = cmp(x.name, y.name) echo people #+end_src #+RESULTS: : @[(name: "Thomas", surname: "Edison"), (name: "Dave", surname: "Smith"), (name: "Mike", surname: "Smith")] #+begin_src nim import sequtils # for map var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"] cities = cities.map do (x: string) -> string: "City of " & x echo cities #+end_src #+RESULTS: : @["City of Frankfurt", "City of Tokyo", "City of New York", "City of Kyiv"] **** Summary of anonymous procs and do notation Below example runs the same ~map~ proc using anonymous procs, and do notations, with and without the command invocation syntax. #+begin_src nim import sequtils # for map var cities1, cities2, cities3, cities4 = @["Frankfurt", "Tokyo", "New York", "Kyiv"] # Anonymous procs echo map(cities1, proc (x: string): string = "City of " & x) echo cities2.map(proc (x: string): string = "City of " & x) # Do notation echo map(cities3) do (x: string) -> string: "City of " & x echo cities4.map do (x: string) -> string: "City of " & x #+end_src #+RESULTS: : @["City of Frankfurt", "City of Tokyo", "City of New York", "City of Kyiv"] : @["City of Frankfurt", "City of Tokyo", "City of New York", "City of Kyiv"] : @["City of Frankfurt", "City of Tokyo", "City of New York", "City of Kyiv"] : @["City of Frankfurt", "City of Tokyo", "City of New York", "City of Kyiv"] #+begin_note ~do~ is written *after the parentheses enclosing the regular proc params.* The proc expression represented by the do block is appended to them. In calls using the command syntax, the do block will bind to the immediately preceeding expression, transforming it in a call. #+end_note ** DONE Blocks CLOSED: [2018-06-04 Mon 14:25] Blocks can be introduced in two different ways: - by indenting statements - with ()s. *** Blocks using indented statements The first way is to use indenting, e.g. using ~if~-~elif~-~else~, ~while~, ~for~ statements, or the ~block~ statement. #+begin_src nim if true: echo "Nim is great!" while false: echo "This line is never output!" block: echo "This line, on the other hand, is always output" #+end_src #+RESULTS: : Nim is great! : This line, on the other hand, is always output The block statement can also be [[#labeled-block][labeled]], making it useful for breaking out of loops. Block statements are useful for general scoping too. #+begin_src nim import strformat, typetraits let b = 3 echo fmt"Outside block: b is of type {b.type.name} of value {b}." block: let b = "4" # shadowing is probably a dumb idea echo fmt" Inside block: b is of type {b.type.name} of value {b}." echo fmt"Outside block: Once again, b is of type {b.type.name} of value {b}." #+end_src #+RESULTS: : Outside block: b is of type int of value 3. : Inside block: b is of type string of value 4. : Outside block: Once again, b is of type int of value 3. *** Blocks using parentheses Parentheses can be used as an expression, but they do not provide the end of statement inference. So it is necessary to place semicolons yourself. An interesting and unexpected side effect of this syntax is that Nim is suitable even for die-hard brace purists! #+begin_note While possible, it doesn't mean it's a good idea. Most Nim code does not use parentheses in that way, and it would not be seen as idiomatic. #+end_note #+begin_src nim proc square(inSeq: seq[float]): seq[float] = ( result = newSeq[float](len(inSeq)); for i, v in inSeq: ( result[i] = v*v; ) ) echo @[2.0, 3, 4].square #+end_src #+RESULTS: : @[4.0, 9.0, 16.0] ** DONE Primitive Types CLOSED: [2018-06-20 Wed 11:06] :PROPERTIES: :CUSTOM_ID: primitive-types :END: Nim has several primitive types: - signed integers :: ~int8~, ~int16~, ~int32~, ~int64~, and ~int~, where ~int~ is the same size as a pointer (see [[#int-types]] for more) - unsigned integers :: similar, but unsigned with "u" prepended to the type - floating point numbers :: ~float32~, ~float64~, and ~float~ (Note that ~float~ and ~float64~ are the same, [[https://github.com/nim-lang/Nim/blob/6efe849769e84124022f5146ddace5c2c10410f9/lib/system.nim#L36-L38][they both refer to the internal ~Float~ type]].) - characters/bytes :: ~char~ and ~byte~, which are basically an alias for ~uint8~ To indicate the size of an integer literal, append "u" or "i" and the size you'd like to the end. Example: ~5'u8~. However, usually this is not necessary.. unless you are dealing with [[#twos-complement][two's complement]] literals. Integers can also have ~0[xX]~, ~0o~, ~0[bB]~ prepended to indicate a hex, octal, or binary literal, respectively. Examples: ~0xABCD~, ~0o567~, ~0b1010~. Underscores are also valid in literals, and can help with readability. #+begin_src nim import strformat, typetraits let a: int8 = 0x7F b: uint8 = 0b1111_1111 c = 0xFF # type is int d = 0o67 # value is written as an octal, type is int e: byte = 62 # f: uint8 = 256 # Compile time error echo fmt"Value of {a.type.name} a is {a}" echo fmt"Value of {b.type.name} b is {b}" echo fmt"Value of {c.type.name} c is {c}" echo fmt"Value of {d.type.name} d is {d}" echo fmt"Value of {e.type.name} e is {e}" #+end_src #+RESULTS: : Value of int8 a is 127 : Value of uint8 b is 255 : Value of int c is 255 : Value of int d is 55 : Value of byte e is 62 *** Signed binary representation / Two's complement :PROPERTIES: :CUSTOM_ID: twos-complement :END: /What is a [[https://en.wikipedia.org/wiki/Two%27s_complement][two's complement]]?/ Below will give compilation error: #+caption: Wrong way of assigning a two's complement #+name: code__twos_complement_wrong #+begin_src nim :eval no let minusOne: int8 = 0b1111_1111 echo minusOne #+end_src #+begin_example nim_src_Kpkxfl.nim(4, 22) Error: type mismatch: got but expected 'int8' #+end_example *Don't do that!* Instead, do: #+caption: Correct way of assigning a two's complement #+name: code__twos_complement_correct #+begin_src nim let minusOne = 0b1111_1111'i8 echo minusOne #+end_src #+RESULTS: code__twos_complement_correct : -1 #+begin_note Explicitly write two's complement notations with the intended type, like ~0b1010_1010'i8~ or ~0xFFFF'i16~. #+end_note In code snippet [[code__twos_complement_wrong]], Nim simply converts binary "1111_1111" to 255 and throws the above error as that obviously cannot be stored as an 8-bit signed integer. Whereas in code snippet [[code__twos_complement_correct]], Nim *directly stores* binary "1111_1111" as *signed 8-bit integer* and auto-recognizes that as a two's complement and evaluates that as decimal "-1". *** Integer Operators Instead of ~^~, ~&~, ~|~, ~>>~, ~<<~ in most other languages, the ~xor~, ~and~, ~or~, ~shr~, ~shl~ operators are used, respectively. #+begin_src nim import strformat, typetraits let a: int = 0b1011_1010 b: int = 0b0101_0011 echo fmt"{a:#010b} ^ {b:#010b} = {(a xor b):#010b}" echo fmt"{a:#010b} & {b:#010b} = {(a and b):#010b}" echo fmt"{a:#010b} | {b:#010b} = {(a or b):#010b}" echo fmt"{a:#010b} >> 3 = {(a shr 3):#b}" echo fmt"{a:#010b} << 5 = {(a shl 5):#b}" #+end_src #+RESULTS: : 0b10111010 ^ 0b01010011 = 0b11101001 : 0b10111010 & 0b01010011 = 0b00010010 : 0b10111010 | 0b01010011 = 0b11111011 : 0b10111010 >> 3 = 0b10111 : 0b10111010 << 5 = 0b1011101000000 ** DONE Type Aliases CLOSED: [2018-06-27 Wed 12:14] :PROPERTIES: :CUSTOM_ID: type-aliases :END: Types are declared inside ~type~ sections, where multiple types can be declared. Note that aliased types are the same as the original types, and so you can have expressions with a mix of original and aliased types. If type safety is desired, [[#distinct-types][distinct types]] should be used. #+begin_src nim type MyInteger = int let a: int = 2 echo a + MyInteger(4) #+end_src #+RESULTS: : 6 ** DONE Object Types CLOSED: [2018-06-27 Wed 16:02] :PROPERTIES: :CUSTOM_ID: object-types :END: In Nim, *objects* are like structs from C family languages and define a grouping of fields. They are by default traced by the garbage collector, so there is no need to explicitly free them when allocated. #+begin_src nim type Animal = object name, species: string age: int proc sleep(a: var Animal) = a.age += 1 proc dead(a: Animal): bool = result = a.age > 20 var carl = Animal(name: "Carl", species: "L. glama", age: 12) let joe = Animal(name: "Joe", species: "H. sapiens", age: 23) assert(not carl.dead) for i in 0 .. 10: carl.sleep() assert carl.dead #+end_src #+RESULTS: Object types are declared in a ~type~ section, as usual. They can be exported, and individual fields can also be exported. #+begin_src nim :eval no type Animal* = object # Export Animal object type name*, species*: string # Export only name and species fields age: int #+end_src Fields can be safely exported without violating encapsulation because call syntax is equivalent between them. Initially, ~carl~ is created on the stack and initialized to zeros, so its value is ~[name = nil, species = nil, age = 0]~. It is mutable (because declared using ~var~), so that means that the contents of ~carl~ can be changed. This also means it can be passed to functions that require a variable parameter, like ~sleep()~, which can modify its value. ~joe~ is also created on the stack, but its contents are immutable (because declared using ~let~) and can not be changed. Attempting to do so, say through ~joe.age = 57~, will fail with an error at compile time. *** Object and References #+begin_src nim type Animal = object name, species: string age: int proc sleep(a: var Animal) = a.age += 1 let mittens: ref Animal = new(Animal) # Everything initialized to 0 or nil echo mittens[] # Initialize all the fields mittens[].name = "Mittens" # When accessing fields of refs, the dereferencing operator [] is # optional. mittens.species = "P. leo" mittens.age = 6 echo mittens[] mittens[].sleep() echo mittens[] #+end_src #+RESULTS: : (name: "", species: "", age: 0) : (name: "Mittens", species: "P. leo", age: 6) : (name: "Mittens", species: "P. leo", age: 7) ~mittens~ is a reference to an object allocated on the *heap* (See [[#heap-stack]]). So the value of ~mittens~ cannot be changed. While ~mittens~ can never point to anything else, the value that ~mittens~ is pointing at can be changed as shown above. The fields in the ~Animal~ object referenced by ~mittens~ are changed from the default initialization value of zeros. As the referenced object (~mittens[]~) is mutable, that can be passed to functions like ~sleep~ that require a variable parameter. Below shows a more concise way of initializing reference types --- by creating a [[#type-aliases][type alias]] ~AnimalRef~ for ~ref Animal~: #+begin_src nim type Animal = object name, species: string age: int AnimalRef = ref Animal proc sleep(a: var Animal) = a.age += 1 var spot = AnimalRef(name: "Spot", species: "C. lupus", age: 1) echo spot[] spot[].sleep() echo spot[] #+end_src #+RESULTS: : (name: "Spot", species: "C. lupus", age: 1) : (name: "Spot", species: "C. lupus", age: 2) In many cases it is only wanted to have the object be a reference type to begin with, which is possible by declaring it as a ~ref object~. #+begin_src nim :eval no type Thing = ref object positionX, positionY: int #+end_src *** ~new~ keyword #+begin_note Try *not* to use the ~new~ keyword to initialize objects. #+end_note From [[https://forum.nim-lang.org/t/3870#24090][this comment]] by {{{nuser(Araq)}}}: #+begin_quote Always prefer the syntax ~student = Student(name: "Anton", age: 5, id: 2)~ over ~new~. ~new~ makes it much harder for the compiler to prove a complete initialization is done. The optimizer will also soon take advantage of this fact. #+end_quote *** More - [[https://forum.nim-lang.org/t/1207][When to use 'ref object' vs plain 'object']] - [[https://forum.nim-lang.org/t/3869][Tuples vs Objects vs Ref Objects?]] Thanks to {{{nuser(yglukhov)}}} for [[https://forum.nim-lang.org/t/3869#24100][this reply]]: #+begin_quote The main conceptual difference between ~smth~ and ~ref smth~ is sharing. You can have several locations pointing to ~ref smth~ and if you change ~smth~ from one location the change will be visible through another location. That's not the case with non - ~ref smth~. So ~ref object~ is not the default -- you have to consciously chose it depending on your design. Also inheritable/polymorphic hierarchies are mostly useful when they are ~ref object~. Size of the objects (and whether it fits on stack) almost never has to be considered in reality. Under the hood, ~ref smth~ implies heap allocation and an extra access indirection. Heap allocation is more expensive than stack allocation, so it might matter in performance critical code paths. But then again, ~refs~ are generally faster to assign to each other, as they take up a single machine word. Tuples are almost like objects. Tuple fields are always public. Tuples can be anonymous, and they can't be inherited. Generally they are used to represent some short living values that don't deserve their own named type. #+end_quote ** DONE Enum Types CLOSED: [2018-07-10 Tue 12:24] Enums in Nim are type-checked. #+name: code__enum_types #+caption: Enum Types (normal, enums with holes, enums with stringified values) #+begin_src nim type CompassDirections = enum cdNorth, cdEast, cdSouth, cdWest Signals = enum # enum with holes sigQuit = 3, # these commas are optional sigAbort = 6, sigKill = 9 Colors {.pure.} = enum Red = "FF0000" # no commas here Green = (1, "00FF00") Blue = "0000FF" var dir: CompassDirections = cdSouth sig: Signals = sigQuit colRed = Colors.Red # qualified enum value reference colGreen = Colors.Green # qualified enum value reference echo dir echo sig echo colRed echo colGreen #+end_src #+RESULTS: code__enum_types : cdSouth : sigQuit : FF0000 : 00FF00 Notice that each element in ~CompassDirections~ is prepended with ~cd~ to avoid name conflicts since references to the enum value do not need to be qualified. Enums can be given custom values as shown by ~Signals~, or even given /stringified/ values, as shown by ~Colors~. /Enums with non-incrementing and non-continuous values like in this ~Signals~ example are called "enums with holes"./ The ~{.pure.}~ pragma that ~Colors~ has, requires that all references to ~Colors~'s values be qualified, therefore making a prefix unnecessary. (Note a bug on /Nim devel/ with ~{.pure.}~ enums -- {{{nimissue(8066)}}}). *** Ordinals :PROPERTIES: :CUSTOM_ID: ordinals :END: Ordinals have [[https://nim-lang.org/docs/system.html#low,typedesc%5BT%5D][~low~]], [[https://nim-lang.org/docs/system.html#high,typedesc%5BT%5D][~high~]], [[https://nim-lang.org/docs/system.html#pred,T,int][~pred~]], [[https://nim-lang.org/docs/system.html#succ,T,int][~succ~]], [[https://nim-lang.org/docs/system.html#dec,T,int][~dec~]], [[https://nim-lang.org/docs/system.html#inc,T,int][~inc~]], and [[https://nim-lang.org/docs/system.html#ord,T][~ord~]] methods defined, where: - ~low~ :: gives the lowest possible value - ~high~ :: gives the highest possible value - ~pred~ :: gives the ordinal value that's /n/ "steps" away from the current value in decreasing order (predecessor) - ~succ~ :: gives the ordinal value that's /n/ "steps" away from the current value in increasing order (successor) - ~dec~ :: decrements the ordinal by /n/ "steps" - ~inc~ :: increments the ordinal by /n/ "steps" - ~ord~ :: gives the integer value of the ordinal #+begin_src nim import strformat, typetraits var someOrdinal = 'A' # char is an ordinal echo fmt"someOrdinal ({$type(someOrdinal)}) = {someOrdinal}" echo fmt"low value of someOrdinal: {repr(someOrdinal.low)}" echo fmt"high value of someOrdinal: {repr(someOrdinal.high)}" echo fmt"value of someOrdinal 1 step away in decrementing order: {someOrdinal.pred} (curr value: {someOrdinal})" echo fmt"value of someOrdinal 3 steps away in decrementing order: {someOrdinal.pred(3)} (curr value: {someOrdinal})" echo fmt"value of someOrdinal 1 step away in incrementing order: {someOrdinal.succ} (curr value: {someOrdinal})" echo fmt"value of someOrdinal 3 steps away in incrementing order: {someOrdinal.succ(3)} (curr value: {someOrdinal})" inc someOrdinal echo fmt"someOrdinal after incrementing once: {someOrdinal}" someOrdinal.inc(2) echo fmt"someOrdinal after incrementing twice: {someOrdinal}" someOrdinal.dec echo fmt"someOrdinal after decrementing once: {someOrdinal}" dec(someOrdinal, 4) echo fmt"someOrdinal after decrementing 4 times: {someOrdinal}" echo fmt"value of someOrdinal: {someOrdinal}" echo fmt"integer value of someOrdinal: {ord(someOrdinal)}" #+end_src #+RESULTS: #+begin_example someOrdinal (char) = A low value of someOrdinal: '\0' high value of someOrdinal: '\255' value of someOrdinal 1 step away in decrementing order: @ (curr value: A) value of someOrdinal 3 steps away in decrementing order: > (curr value: A) value of someOrdinal 1 step away in incrementing order: B (curr value: A) value of someOrdinal 3 steps away in incrementing order: D (curr value: A) someOrdinal after incrementing once: B someOrdinal after incrementing twice: D someOrdinal after decrementing once: C someOrdinal after decrementing 4 times: ? value of someOrdinal: ? integer value of someOrdinal: 63 #+end_example Enums are *ordinals* too *if* their values are incrementing and continuous (incrementing by 1, no holes). So all of those methods for ordinals would work for such enums too. #+begin_src nim import strformat, typetraits type CompassDirections = enum cdNorth, cdEast, cdSouth, cdWest for direction in ord(low(CompassDirections)) .. ord(high(CompassDirections)): echo fmt"{CompassDirections(direction)} ({$type(CompassDirections(direction))}), " & fmt"ord: {direction} ({$type(direction)})" let cDir = cdEast echo cDir.pred # int arg defaults to 1 echo cDir.succ(2) # int arg defaults to 1 #+end_src #+RESULTS: : cdNorth (CompassDirections), ord: 0 (int) : cdEast (CompassDirections), ord: 1 (int) : cdSouth (CompassDirections), ord: 2 (int) : cdWest (CompassDirections), ord: 3 (int) : cdNorth : cdWest ~CompassDirections(direction)~ is a type conversion that gives the ~CompassDirections~ enum from the integer ~direction~. It is possible to iterate through all possible values of ordinal enums, either as shown above, or ~cdNorth .. cdWest~, which is equivalent. *** Enums with holes :PROPERTIES: :CUSTOM_ID: holey-enums :END: Enums can also have disjoint values i.e. their values do not have to be incrementing by 1 in succession --- I call them /"holey enums"/. #+begin_note Enums with holes should not be used for any other reason than compatibility with C because it breaks the idea that enums are ordinals! #+end_note The ~Signals~ enum example in [[code__enum_types]] is not an ordinal type enum as its values are non-continuous. As per the [[https://nim-lang.org/docs/manual.html#types-enumeration-types][Nim Manual]]: #+begin_quote An explicit ordered enum can have holes. However, it is then not an ordinal anymore, so it is not possible to use these enums as an index type for arrays. The procedures ~inc~, ~dec~, ~succ~ and ~pred~ are not available for them either. #+end_quote **** FIXED Ordinal methods on enums with holes CLOSED: [2018-07-12 Thu 12:32] :PROPERTIES: :CUSTOM_ID: issue-8262 :END: Thanks to {{{guser(LemonBoy)}}} for fixing this in {{{nimpr(8264)}}}. Now when trying to run the below snippet: #+begin_src nim :eval no type Signals = enum sigQuit = 3, sigAbort = 6, sigKill = 9 var nonOrdinal = sigQuit inc nonOrdinal #+end_src will give this error: #+begin_example nim_src_xNvIoW.nim(9, 5) Error: type mismatch: got but expected one of: proc inc[T: Ordinal | uint | uint64](x: var T; y = 1) expression: inc nonOrdinal #+end_example ***** Older issue Contrary to the above quote from the Nim manual, below shows that ~inc~, ~dec~, ~succ~, ~pred~ procedures do work with such "holey enums"; albeit printing "invalid data!" when those operations end up on holes -- {{{nimissue(8262)}}}, {{{nimissue(1239)}}}. #+begin_src nim :eval no type Signals = enum sigQuit = 3, sigAbort = 6, sigKill = 9 var nonOrdinal = sigQuit echo " ", nonOrdinal echo "inc on holey enums" inc nonOrdinal echo " ", nonOrdinal inc nonOrdinal echo " ", nonOrdinal inc nonOrdinal echo " ", nonOrdinal inc nonOrdinal echo " ", nonOrdinal echo "dec on holey enums" dec nonOrdinal echo " ", nonOrdinal dec nonOrdinal echo " ", nonOrdinal echo "succ on holey enums" echo " ", nonOrdinal.succ echo " ", nonOrdinal.succ(2) echo "pred on holey enums" echo " ", nonOrdinal.pred #+end_src #+RESULTS: #+begin_example sigQuit inc on holey enums 4 (invalid data!) 5 (invalid data!) sigAbort 7 (invalid data!) dec on holey enums sigAbort 5 (invalid data!) succ on holey enums sigAbort 7 (invalid data!) pred on holey enums 4 (invalid data!) #+end_example ** DONE Distinct Types CLOSED: [2018-06-27 Wed 12:37] :PROPERTIES: :CUSTOM_ID: distinct-types :END: Distinct types are like [[#type-aliases][type aliases]], but they provide type safety so that it is impossible to coerce a distinct type into its base type without explicit conversion. Below does not compile even if ~a~ is being assigned a float value, because the type of ~a~ is a *distinct* type alias of ~float~. So ~float~ and ~Dollars~ are technically /distinct/ types even if they are aliases. #+begin_src nim :eval no type Dollars = distinct float var a = Dollars(20) a = 25.0 # Doesn't compile #+end_src #+begin_example nim_src_YftC2H.nim(8, 3) Error: type mismatch: got but expected 'Dollars = distinct float' #+end_example But the below compiles: #+begin_src nim :hl_lines 5 type Dollars = distinct float var a = Dollars(20) a = Dollars(25.0) echo float(a) #+end_src #+RESULTS: : 25.0 Notice that in the above snippet, I used ~echo float(a)~. That's because ~echo a~ will not work automatically! None of the base type's procedures will work for the ~distinct~ type -- for example ~echo~, ~+~, ~*~. As ~Dollars~ is a ~distinct~ type, its ~$~ needs to be defined too! So the below works: #+begin_src nim :hl_lines 4,5,9 type Dollars = distinct float proc `$`(d: Dollars): string = $float(d) # convert Dollars to float and return stringified float var a = Dollars(20) a = Dollars(25.0) echo a #+end_src #+RESULTS: : 25.0 *** ~borrow~ pragma **** ~borrow~ for procs If we do this, we will end up writing lots of thin wrappers for all such commonly used procedures. So another way is to use the ~{.borrow.}~ pragma instead, which automates the generation of such procedures borrowed from the base types. #+begin_src nim :hl_lines 6-8,11-13 import typetraits, strformat type Dollars = distinct float proc `$`(d: Dollars): string {.borrow.} proc `*`(a, b: Dollars): Dollars {.borrow.} proc `+`(a, b: Dollars): Dollars {.borrow.} var a = Dollars(25.0) echo fmt"a of type {$a.type.name} = {a}" a = 10.Dollars * (20.Dollars + 1.Dollars) echo fmt"a of type {$a.type.name} = {a}" #+end_src #+RESULTS: : a of type Dollars = 25.0 : a of type Dollars = 210.0 /The above example failed to compile in Nim 0.19.0, but was soon fixed in {{{nimcommit(959e3a08)}}} by {{{guser(LemonBoy)}}}./ **** ~borrow~ for object fields When creating a ~distinct~ type from an object type, none of its fields are carried over. If the fields are wanted, they can be brought over through an overloading of the ~{.borrow.}~ pragma. If they are not borrowed, they cannot be accessed. Below example basically /borrows/ the ~.~ operator from the base type ~Foo~ so that field access for the derived ~distinct~ type ~MyFoo~ works too. #+begin_src nim type Foo = object a: int MyFoo {.borrow: `.`.} = distinct Foo var value: MyFoo value.a = 100 echo value.a #+end_src #+RESULTS: : 100 Without that "dot" /borrow/, you would get this error: #+begin_example nim_src_PlBYmp.nim(10, 11) Error: undeclared field: 'a' #+end_example ** DONE Strings CLOSED: [2018-07-03 Tue 18:25] :PROPERTIES: :CUSTOM_ID: strings :END: There are several types of string literals: - Quoted Strings :: Created by wrapping the body in triple quotes (~""" .. """~). They never interpret escape codes. #+begin_src nim echo """ \n\n """ #+end_src #+RESULTS: : : : \n\n : : : : - Raw Strings :: Created by prefixing the string with an ~r~. They do not interpret escape sequences either, except for ~""~, which is interpreted as ~"~. This means that ~r"\b[a-z]\b"~ is interpreted literally as ~\b[a-z]\b~. #+begin_src nim echo r".""." echo r"\b[a-z]\b" #+end_src #+RESULTS: : .". : \b[a-z]\b - Proc Strings :: Same as raw strings, but the proc name is prefixed directly to the string, so that ~foo"12"~ implies ~foo(r"12")~. *Note that the proc has to be called as ~PROC"STRING"~ i.e. no parentheses.* #+begin_src nim proc foo(s: string): string = s echo foo".""." # echo foo("."".") # This will not work; will give compilation error. echo foo"\b[a-z]++\b" #+end_src #+RESULTS: : .". : \b[a-z]++\b Strings are *null-terminated*, so that ~cstring("foo")~ requires zero copying. However, you should be careful that the lifetime of the /cstring/ does not exceed the lifetime of the string it is based upon. Strings can also almost be thought of as ~seq[char]~ with respect to assignment semantics. See [[#nim-by-example-seqs]]. ** DONE Arrays CLOSED: [2018-07-11 Wed 14:24] :PROPERTIES: :CUSTOM_ID: nim-by-example-arrays :END: This section has more examples for Nim arrays. See [[#arrays]] for more info. The size of arrays in Nim has to be specified at compile-time and cannot be given or changed at runtime. The size of the array is encoded in its type and cannot be accidentally lost. Therefore, a procedure taking an array of variable length must encode the length in its type parameters. Alternatively, such procedures can also use [[* Open Arrays][openArrray]] as the parameter type, which the array length does not need to be specified. #+begin_src nim type ThreeStringAddress = array[3, string] let names: ThreeStringAddress = ["Jasmine", "Ktisztina", "Kristof"] let addresses: ThreeStringAddress = ["101 Betburweg", "66 Bellion Drive", "194 Laarderweg"] echo names echo addresses proc zip[I, T](a, b: array[I, T]): array[I, tuple[a, b: T]] = for i in low(a) .. high(a): result[i] = (a[i], b[i]) let nameAndAddresses = names.zip(addresses) echo nameAndAddresses #+end_src #+RESULTS: : ["Jasmine", "Ktisztina", "Kristof"] : ["101 Betburweg", "66 Bellion Drive", "194 Laarderweg"] : [(a: "Jasmine", b: "101 Betburweg"), (a: "Ktisztina", b: "66 Bellion Drive"), (a: "Kristof", b: "194 Laarderweg")] The first type parameter of an array is actually a range like ~0 .. N-1~ (just a value "3" as in the above example is syntactic sugar for ~0 .. 2~). It's also possible to use ordinal values to index an array, effectively creating a lookup table. See [[#enum-length-arrays]] for more. *** Nested Array #+begin_src nim type Matrix[W, H: static[int]] = array[1 .. W, array[1 .. H, int]] let mat1: Matrix[2, 2] = [[1, 0], [0, 2]] mat2: Matrix[2, 2] = [[0, 3], [4, 0]] proc `+`[W, H](a, b: Matrix[W, H]): Matrix[W, H] = for i in 1 .. high(a): for j in 1 .. high(a[0]): result[i][j] = a[i][j] + b[i][j] echo mat1 + mat2 #+end_src #+RESULTS: : [[1, 3], [4, 2]] - ~static[int]~ in the above example is a /static type/. The ~static[T]~ construct is needed as the array size needs to be known at compile time. See [[https://nim-lang.org/docs/manual.html#special-types-static-t][Nim Manual -- ~static{T}~]] for more. ** DONE Seqs CLOSED: [2018-07-11 Wed 14:50] :PROPERTIES: :CUSTOM_ID: nim-by-example-seqs :END: Sequences (/Seqs/ for short) provide dynamically expandable storage. - There are two ways to create sequences: - with the ~@~ operator, and - with the ~newSeq[T](n: int)~ method - Once a sequence is created, it can be modified using methods like ~add(item: T)~ and ~delete(idx: int)~. - The length of a seq can be found through ~len: int~, and the maximum index through ~high: int~. - The standard ~items: T~ and ~pairs: tuple[i: int, v: T]~ iterators are also available. #+begin_src nim var a = @[1, 2, 3] b = newSeq[int](3) echo a for i, v in a: b[i] = v*v echo b for i in 4 .. 15: b.add(i * i) echo b b.delete(0) # takes O(n) time echo b b = a[0] & b # Same as original b echo b #+end_src #+RESULTS: : @[1, 2, 3] : @[1, 4, 9] : @[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225] : @[4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225] : @[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225] *** Immutability Sequences are dynamically allocated (i.e. allocated on the heap, not the stack), but they are immutable (like any other Nim variable) unless marked as ~var~. So the below will fail to compile as ~a~ cannot be assigned to: #+begin_src nim :eval no let a = @[1, 2, 3] a.add(4) #+end_src However, the below will work without any problem: #+begin_src nim var b = @[1, 2, 3] b.add(4) echo b #+end_src #+RESULTS: : @[1, 2, 3, 4] Sequences passed as "argument by value" are not modifiable. For example, the following will fail to compile: #+begin_src nim :eval no proc doSomething(mySeq: seq[int]) = mySeq[0] = 2 # Error: 'mySeq[0]' cannot be assigned to #+end_src Sequence arguments can be mutable if they are passed as "argument by *reference*", i.e. the parameter is annotated with ~var~ or ~ref~: #+begin_src nim proc foo(mySeq: var seq[int]) = mySeq[9] = 999 var thisSeq = newSeq[int](10) foo(thisSeq) echo thisSeq #+end_src #+RESULTS: : @[0, 0, 0, 0, 0, 0, 0, 0, 0, 999] If the sequence needs to be passed as value, and not reference, you can first copy that sequence inside the proc and then modify: #+begin_src nim proc doSomething(mySeq: seq[int]) = var varMySeq = mySeq # copy the seq varMySeq[0] = 999 echo "Inside the proc: varMySeq = ", varMySeq var testSeq = @[1, 2, 3] echo "Before the proc: testSeq = ", testSeq doSomething(testSeq) echo "After the proc: testSeq = ", testSeq #+end_src #+RESULTS: : Before the proc: testSeq = @[1, 2, 3] : Inside the proc: varMySeq = @[999, 2, 3] : After the proc: testSeq = @[1, 2, 3] Above, as the ~doSomething~ proc is not returning the modified value ~varMySeq~, the input argument sequence ~testSeq~ remains unmodified. *** More about sequences See [[#sequences]]. ** TODO Bitsets *** Check if a character is in a character set (~in~) #+begin_src nim echo "'d' is lower? ", 'd' in {'a' .. 'z'} echo "'D' is lower? ", 'D' in {'a' .. 'z'} echo "'D' is upper? ", 'D' in {'A' .. 'Z'} #+end_src #+RESULTS: : 'd' is lower? true : 'D' is lower? false : 'D' is upper? true *** Check if a character is *not* in a character set (~notin~) #+begin_src nim echo "'d' is not lower? ", 'd' notin {'a' .. 'z'} echo "'D' is not lower? ", 'D' notin {'a' .. 'z'} echo "'D' is not upper? ", 'D' notin {'A' .. 'Z'} echo "'.' is neither upper not lower? ", '.' notin {'A' .. 'Z', 'a' .. 'z'} #+end_src #+RESULTS: : 'd' is not lower? false : 'D' is not lower? true : 'D' is not upper? false : '.' is neither upper not lower? true ** TODO Varargs ** TODO Object Oriented Programming ** TODO OOP Macro * Nim in Action ** Threads *** 6.2.1 The =threads= module and GC safety **** Problematic code #+begin_src nim :flags --threads:on :eval no var data = "Hello World" proc showData() {.thread.} = echo(data) var thread: Thread[void] createThread[void](thread, showData) joinThread(thread) #+end_src #+results: : nim_src_I3YWGm.nim(6, 6) Error: 'showData' is not GC-safe as it : accesses 'data' which is a global using GC'ed memory /Remove =:eval no= to see the above error./ **** Fixed code #+begin_src nim :flags --threads:on var data = "Hello World" proc showData(param: string) {.thread.} = echo(param) var thread: Thread[string] createThread[string](thread, showData, data) joinThread(thread) #+end_src #+results: : Hello World ** Parsing *** 6.3.2 Parsing the Wikipedia page counts format **** Using regular expressions #+begin_src nim import re let pattern = re"([^\s]+)\s([^\s]+)\s(\d+)\s(\d+)" var line = "en Nim_(programming_language) 1 70231" var matches: array[4, string] let start = find(line, pattern, matches) doAssert start == 0 doAssert matches[0] == "en" doAssert matches[1] == "Nim_(programming_language)" doAssert matches[2] == "1" doAssert matches[3] == "70231" echo "Parsed successfully!" #+end_src #+results: : Parsed successfully! **** Parsing the data manually using =split= #+begin_src nim import strutils var line = "en Nim_(programming_language) 1 70231" var matches = line.split() doAssert matches[0] == "en" doAssert matches[1] == "Nim_(programming_language)" doAssert matches[2] == "1" doAssert matches[3] == "70231" echo "Parsed successfully!" #+end_src #+results: : Parsed successfully! **** Parsing the data manually using =parseutils= #+begin_src nim import parseutils # https://nim-lang.org/docs/parseutils.html var line = "en Nim_(programming_language) 1 70231" var i = 0 var domainCode = "" # parseUntil(s, token, until, start) # returns the number of parsed characters i.inc parseUntil(line, domainCode, {' '}, i) i.inc var pageTitle = "" i.inc parseUntil(line, pageTitle, {' '}, i) i.inc var countViews = 0 i.inc parseInt(line, countViews, i) i.inc var totalSize = 0 i.inc parseInt(line, totalSize, i) i.inc doAssert domainCode == "en" doAssert pageTitle == "Nim_(programming_language)" doAssert countViews == 1 doAssert totalSize == 70231 echo "Parsed successfully!" #+end_src #+results: : Parsed successfully! * Unit testing ** Unit Testing Common code :noexport: #+begin_src nim :noweb-ref card_suite_common :eval no type Card = object rank: Rank suit: Suit Rank = enum crSeven crEight crNine crTen crJack crQueen crKing crAce Suit = enum csClubs = "♧" csDiamonds = "♢" csHearts = "♡" csSpades = "♤" #+end_src #+begin_src nim :noweb-ref card_suite_unittest :eval no import unittest suite "test card relations": setup: let aceDiamonds = Card(rank: crAce, suit: csDiamonds) kingClubs = Card(rank: crKing, suit: csClubs) aceClubs = Card(rank: crAce, suit: csClubs) test "greater than": check: aceDiamonds > kingClubs aceClubs > kingClubs test "equal to": check aceDiamonds == aceClubs #+end_src ** Using plain ~assert~ in ~isMainModule~ :PROPERTIES: :CUSTOM_ID: card-suite-assert :END: #+begin_src nim :noweb yes :eval no <> proc `<`(a,b: Card): bool = a.rank < b.rank when isMainModule: let aceDiamonds = Card(rank: crAce, suit: csDiamonds) kingClubs = Card(rank: crKing, suit: csClubs) aceClubs = Card(rank: crAce, suit: csClubs) assert aceDiamonds > kingClubs assert aceDiamonds == aceClubs #+end_src #+RESULTS: Evaluating above will throw this error: #+begin_example Error: unhandled exception: aceDiamonds == aceClubs [AssertionError] #+end_example Above ~<~ is implemented for ~Cards~. The ~>~ operator is just sugar for backwards ~<~. ~isMainModule~ is used to do some quick tests. It means that if this module were to be imported and used in some application, that logic wouldn't even be compiled into the binary, let alone evaluated. ** Using the ~unittest~ module #+begin_src nim :noweb yes :tangle "code/unittest_fail.nim" :mkdirp yes :eval no <> proc `<`(a,b: Card): bool = a.rank < b.rank when isMainModule: <> #+end_src *** Breakdown of the ~unittest~ code We first define the /test suite/ name. #+begin_src nim :eval no suite "test card relations": #+end_src In that test suite, we do some initial /setup/, that defines the variables, etc. that will be used in the tests. #+begin_src nim :eval no setup: let aceDiamonds = Card(rank: crAce, suit: csDiamonds) kingClubs = Card(rank: crKing, suit: csClubs) aceClubs = Card(rank: crAce, suit: csClubs) #+end_src Then we define /tests/ using ~test "test name":~, and use the ~check~ statements nested under those to do the checks, which look similar to the earlier [[#card-suite-assert][~assert~ approach]]. #+begin_src nim :eval no # test 1 test "greater than": check: aceDiamonds > kingClubs aceClubs > kingClubs # test 2 test "equal to": check aceDiamonds == aceClubs #+end_src *** Failure output ~unittest~ produces a more informative failure output as shown below. **** TO__BE__FIXED [ob-nim or unittest] Above does not create the ~unittest~ error when evaluated using ob-nim :PROPERTIES: :CUSTOM_ID: ob-nim-unittest-error :END: But as you see in the [[#card-suite-corrected-code][Corrected code]] section, the ~unittest~ generated "pass" message gets output fine. Issue filed at https://github.com/Lompik/ob-nim/issues/4. ----- /For now the error message returned by ~unittest~ is pasted below manually, until I start getting it correctly on *stderr* via ~unittest~./ #+begin_example [Suite] test card relations [OK] greater than cardsuite.nim(39, 24): Check failed: aceDiamonds == aceClubs aceDiamonds was (rank: crAce, suit: ♢) aceClubs was (rank: crAce, suit: ♧) [FAILED] equal to #+end_example *** Corrected code :PROPERTIES: :CUSTOM_ID: card-suite-corrected-code :END: From the error, we see that only the "equal to" test fails. As the ~==~ proc is not defined for ~Card~, Nim used the default ~==~ for object comparison, and so that ~check aceDiamonds == aceClubs~ test failed. #+begin_src nim :noweb yes <> proc `<`(a,b: Card): bool = a.rank < b.rank proc `==`(a,b: Card): bool = a.rank == b.rank when isMainModule: <> #+end_src #+RESULTS: : : [Suite] test card relations : [OK] greater than : [OK] equal to *** About DSL and ~unittest~ Nim is extremely effective at creating [[https://en.wikipedia.org/wiki/Domain-specific_language][DSLs]] in ways that other languages simply don't have at their disposal. Statements like ~check~ and ~suite~ in this ~unittest~ module almost act like additions to the syntax of the language itself; they're not functions or classes, operating at runtime; they operate at compilation, accepting the tests written by the programmer as AST objects and manipulating them until the resulting code is quite different indeed. ** Skipping tests Put ~skip~ or ~skip()~ at the *end* of the ~test~ block you want to skip. #+begin_src nim import unittest suite "something": test "passing test": check: true == true test "failing test to be ignored": check: true == false skip #+end_src #+RESULTS: : : [Suite] something : [OK] passing test : /tmp/babel-fDrjcl/nim_src_r0hnsj.nim(13, 11): Check failed: true == false : true was true : false was false : [SKIPPED] failing test to be ignored ** Expecting failures (~expect~) [[https://nim-lang.org/docs/unittest.html#expect.m,varargs%5Btyped%5D,untyped][unittest -- ~expect~]] #+begin_src nim :tangle "expected_error.nim" import unittest suite "something": test "passing test": check: true == true test "test expected to fail": expect AssertionError: assert true == false #+end_src #+RESULTS: : : [Suite] something : [OK] passing test : [OK] test expected to fail Thanks to {{{guser(mratsim)}}} for [[https://gitter.im/nim-lang/Nim?at=5b3a86379b82c6701b9cbae7][this tip]]. ** Also see - [[https://github.com/jyapayne/einheit][~einheit~ -- Nim unit test library]] ** Reference - ~unittest.nim~ -- [[https://github.com/nim-lang/Nim/blob/master/lib/pure/unittest.nim][Source]] | [[https://nim-lang.org/docs/unittest.html][Documentation]] - https://blog.zdsmith.com/posts/unit-testing-in-nim.html#unittestinginnim * Documentation ** TODO ~runnableExamples~ See [[https://github.com/narimiran/itertools][~itertools~ library]] as an example. * COMMENT Deployment ** Build on Travis and deploy Example: https://github.com/Calinou/clr/blob/master/.gitlab-ci.yml About the ~--passc:"-flto"~ switch ([[https://gitter.im/nim-lang/Nim?at=5b54c206f477e4664ab535bd][ref]]): #+begin_quote it enables link-time optimization (smaller/faster binary, at the cost of slower linking and increased RAM usage while linking) -y accepts all prompts :⁠) #+end_quote #+begin_src yaml :eval no stages: - build - deploy build:linux: stage: build image: fedora:28 before_script: - dnf install gcc git xz -y - export CHOOSENIM_NO_ANALYTICS=1 - export CHOOSENIM_CHOOSE_VERSION="stable" - curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh - sh init.sh -y script: - export PATH="$HOME/.nimble/bin:$PATH" - mkdir -p dist/ - nimble build -d:release --passc:"-flto" -y - strip clr - tar cfJ dist/clr-linux-x86_64.tar.xz clr artifacts: expire_in: 30 days paths: - dist/ deploy:linux: stage: deploy image: fedora:28 dependencies: - build:linux only: - tags before_script: - dnf install golang -y - go get github.com/itchio/gothub script: - export PATH="$HOME/go/bin:$PATH" - gothub release --user Calinou --repo clr --tag "$CI_COMMIT_TAG" - gothub upload --user Calinou --repo clr --tag "$CI_COMMIT_TAG" --name "clr-$CI_COMMIT_TAG-linux-x86_64.tar.xz" --file "dist/clr-linux-x86_64.tar.xz" #+end_src * Miscellaneous ** Checking if something compiles (~compiles~) :PROPERTIES: :CUSTOM_ID: compiles :END: From [[https://nim-lang.org/docs/system.html#compiles,untyped][docs]]: #+begin_quote Special compile-time procedure that checks whether /x/ can be compiled without any semantic error. This can be used to check whether a type supports some operation. #+end_quote #+begin_src nim when compiles(int): echo "int type exists" when not compiles(Foo): echo "Foo type does not exist" when compiles(3 + 4): echo "'+' for integers is available" when not compiles(3 ... 4): echo "'...' for integers is not available" #+end_src #+RESULTS: : int type exists : Foo type does not exist : '+' for integers is available : '...' for integers is not available Thanks to {{{guser(mratsim)}}} for [[https://gitter.im/nim-lang/Nim?at=5b3a86379b82c6701b9cbae7][this tip]]. ** Checking if an /identifier/ is declared :PROPERTIES: :CUSTOM_ID: declared :END: From the [[https://nim-lang.org/docs/system.html#declared,untyped][docs]]: #+begin_quote Special compile-time procedure that checks whether x is declared. x has to be an identifier or a qualified identifier. This can be used to check whether a library provides a certain feature or not. #+end_quote The compile-time procedure ~declared~ returns ~true~ if a variable/type/proc/etc. is declared. *** ~declared~ and variable ~declared(foo)~ returns /true/ even if ~foo~ is not explicitly initialized. #+begin_src nim var foo: bool bar: bool = true echo "Is the uninitialized variable 'foo' declared? ", declared(foo) echo "Is the initialized variable 'bar' declared? ", declared(bar) echo "Is the undeclared variable 'zoo' declared (duh)? ", declared(zoo) #+end_src #+RESULTS: : Is the uninitialized variable 'foo' declared? true : Is the initialized variable 'bar' declared? true : Is the undeclared variable 'zoo' declared (duh)? false *** ~declared~ and type ~declared~ is also used to check if something is a valid type. #+begin_src nim type Foo = int echo "Is 'Foo' declared? ", declared(Foo) #+end_src #+RESULTS: : Is 'Foo' declared? true This is useful to check for a type that could be introduced in a new Nim version -- ~SomeFloat~ replaced the ~SomeReal~ type in Nim 0.19.0. So the below code can be used to define the new ~SomeFloat~ type only for older versions (without needing to do explicit version checks): #+begin_src nim :eval no when not declared SomeFloat: type SomeFloat = SomeReal #+end_src Also see [[#compiles][~compiles~]]. *** ~declared~ and proc .. and the same for procs. #+begin_src nim proc Foo() = discard echo "Is 'Foo' declared? ", declared(Foo) #+end_src #+RESULTS: : Is 'Foo' declared? true ** Checking if a compilation switch is present (~defined~) :PROPERTIES: :CUSTOM_ID: defined :END: Special compile-time proc to check if a certain ~-d:foo~ or ~--define:foo~ switch was used during compilation. From [[https://nim-lang.org/docs/system.html#defined,untyped][Nim Docs -- ~defined~]]: #+begin_quote Special compile-time procedure that checks whether /x/ is defined. /x/ is an external symbol introduced through the compiler's *-d:x* switch to enable build time conditionals: #+begin_src nim :eval no when not defined(release): # Do here programmer friendly expensive sanity checks. # Put here the normal code #+end_src #+end_quote For example, below code was compiled and run using ~nim c -r -d:foo --define:zoo test_defined.nim~: #+begin_src nim :flags -d:foo --define:zoo echo "Is '-d:foo' or '--define:foo' flag passed to the compiler? ", defined(foo) echo "Is '-d:bar' or '--define:bar' flag passed to the compiler? ", defined(bar) echo "Is '-d:zoo' or '--define:zoo' flag passed to the compiler? ", defined(zoo) #+end_src #+RESULTS: : Is '-d:foo' or '--define:foo' flag passed to the compiler? true : Is '-d:bar' or '--define:bar' flag passed to the compiler? false : Is '-d:zoo' or '--define:zoo' flag passed to the compiler? true Thanks to {{{guser(Yardanico)}}} and {{{guser(mratsim)}}} to help understand this proc's use. Unlike regular Nim identifiers, the tokens for ~defined~ are *completely* case-insensitive *and* underscore-insensitive (/as long as the token doesn't begin or end with one/). For example, below code was compiled and run using ~nim c -r -d:foo test_defined2.nim~: #+begin_src nim :flags -d:foo echo "Is '-d:foo' or '--define:foo' flag passed to the compiler? ", defined(foo) echo "Is '-d:foo' or '--define:foo' flag passed to the compiler? ", defined(Foo) echo "Is '-d:foo' or '--define:foo' flag passed to the compiler? ", defined(foO) echo "Is '-d:foo' or '--define:foo' flag passed to the compiler? ", defined(F_O_o) # echo "Is '-d:foo' or '--define:foo' flag passed to the compiler? ", defined(_foo) # Error: invalid token: _ (\95) # echo "Is '-d:foo' or '--define:foo' flag passed to the compiler? ", defined(foo_) # Error: invalid token: trailing underscore #+end_src #+RESULTS: : Is '-d:foo' or '--define:foo' flag passed to the compiler? true : Is '-d:foo' or '--define:foo' flag passed to the compiler? true : Is '-d:foo' or '--define:foo' flag passed to the compiler? true : Is '-d:foo' or '--define:foo' flag passed to the compiler? true ** Getting the values of compile-time ~-d:~ switches :PROPERTIES: :CUSTOM_ID: define-switch-value :END: - Ref :: [[https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-compile-time-define-pragmas][compile time define pragmas]] The compile-time define switch (~-d:~ or ~--define:~) values can be set using: - ~-d:FOO=VAL~ / ~--define:FOO=VAL~ /(my preferred way)/ - ~-d:FOO:VAL~ / ~--define:FOO:VAL~ *** Parsing an integer define switch If a define switch is supposed to accept integer values (the integer value is parsed using the ~{.intdefine.}~ pragma), it *has to be explicitly assigned a value*. When below is compiled with ~-d:foo~ (i.e. without assigning any value to the define switch): #+begin_src nim :flags -d:foo :eval no const foo {.intdefine.}: int = 0 # default value = 0 echo foo #+end_src you get this error: #+begin_example nim_src_fUgVdc.nim(5, 6) Error: expression is not an integer literal #+end_example You need to pass a value to the /integer/ define switch. For example, compiling the same with ~-d:foo=123~ gives: #+begin_src nim :flags -d:foo=123 const foo {.intdefine.}: int = 0 # default value = 0 echo foo #+end_src #+RESULTS: : 123 *** Parsing a string define switch Unlike integer define-switches, string define-switches (the string value is parsed using the ~{.strdefine.}~ pragma) do not need the value to be set. #+begin_note In the case of unspecified value, it is auto-set to ~"true"~ string. So ~-d:foo~ and ~-d:foo=true~ are treated the same. #+end_note Below is compiled with ~-d:foo=true~: #+begin_src nim :flags -d:foo=true const foo {.strdefine.}: string = "" # default value = "" echo foo #+end_src #+RESULTS: : true Below is compiled with just ~-d:foo~. But notice that here too, the value of the ~foo~ /const/ gets set to ~"true"~. #+begin_src nim :flags -d:foo const foo {.strdefine.}: string = "" # default value = "" echo foo #+end_src #+RESULTS: : true And below is compiled with ~-d:foo=abc~: #+begin_src nim :flags -d:foo=abc const foo {.strdefine.}: string = "" # default value = "" echo foo #+end_src #+RESULTS: : abc ** Changing the ~nimcache/~ directory Below tip by {{{guser(zah)}}} shows how to create the ~nimcache/~ directory in ~/tmp~ by default. - https://github.com/nim-lang/Nim/issues/7402#issuecomment-376153970 - https://forum.nim-lang.org/t/3897 You can place this in ~/config/nim.cfg~ or in =~/.config/nim.cfg=: #+begin_src conf :eval no @if unix: @if not release: nimcache = "/tmp/nimcache/d/$projectName" @else: nimcache = "/tmp/nimcache/r/$projectName" @end @end @if windows: @if not release: nimcache = r"C:\Temp\nimcache\d\$projectName" @else: nimcache = r"C:\Temp\nimcache\r\$projectName" @end @end #+end_src ** Compiling in 32-bit mode Thanks to [[https://gitter.im/nim-lang/Nim?at=5b2aa6ba467bd7268c20c3db][this tip]] by {{{guser(Yardanico)}}}, a Nim program can be compiled in 32-bit mode by doing: #+begin_example nim c --cpu:i386 --passC:-m32 --passL:-m32 foo.nim #+end_example He provides a disclaimer to that saying "assuming your GCC is multilib". /I believe that's the case -- I don't know! -- because that worked/ :smile: From [[#int-types]], one way to check if the compilation is happening in 32-bit vs 64-bit is to look at the size of the *int* type. In 32-bit compilation: #+begin_src nim :flags --cpu:i386 --passC:-m32 --passL:-m32 import strformat echo fmt"Size of int = {sizeof(int)} bytes" #+end_src #+RESULTS: : Size of int = 4 bytes In 64-bit compilation: #+begin_src nim import strformat echo fmt"Size of int = {sizeof(int)} bytes" #+end_src #+RESULTS: : Size of int = 8 bytes ** Splitting Org header args #+begin_src nim import strutils let args = """#+begin_src nim :tangle "foo.nim" :shebang "#!/usr/bin/env bash"""" echo args.split(" ") # parses arg vals with spaces incorrectly echo args.split(":") # better echo args.split(":")[0].strip.split(" ") #+end_src #+RESULTS: : @["#+begin_src", "nim", ":tangle", "\"foo.nim\"", ":shebang", "\"#!/usr/bin/env", "bash\""] : @["#+begin_src nim ", "tangle \"foo.nim\" ", "shebang \"#!/usr/bin/env bash\""] : @["#+begin_src", "nim"] * TODO To be properly organized :noexport: * NEED__TO__UNDERSTAND Pragmas - https://forum.nim-lang.org/t/2751 ** ~noinit~ Prevents auto-initialization of *local* variables. So such variables would then hold random values. #+begin_src nim proc a() = var ai {.noinit.}: array[5, int] echo ai a() #+end_src #+RESULTS: : [140725905509408, 0, 140725905509440, 140725905509456, 140725905509544] See [[*Uninitialized variables]] for details. ** ~dirty~ I got curious why ~{.dirty.}~ pragma was added in [[https://github.com/NimParsers/parsetoml/commit/89329bec4625d2c9d4559c65e4ec5a1ff402531a][this ~parsetoml~ commit]]: #+begin_src nim :eval no template defineGetProcDefault(name: untyped, t: typeDesc, doccomment: untyped) {.dirty.} = proc name*(table: TomlTableRef, address: string, default: t): t = .. #+end_src and {{{guser(PMunch)}}} gave this nice explanation: #+begin_quote Normally a ~template~ is what you call hygienic. This means that none of the symbols in the template is visible outside of it, and will not clutter the namespace of the context it is called in or interfere with anything. Imagine calling a template twice which instantiates a temporary variable for example, this would fail if the template wasn't hygienic as the variable would already exist when the template was called the second time. In this case we only use the template to generate some procedures, and we want those to be injected into the main scope as is. The problem before was that the ~table~, ~address~, and ~default~ arguments would get changed to something like ~table243829~, ~address231984~, and ~default290432~ in the generated code. This would mean that if you wanted to name your arguments you would have to use those names, not very pretty. Plus the documentation used those names and looked over-all a bit ugly. By marking the template as ~{.dirty.}~ we stop this behaviour and the argument names stay the same. Since all these templates does is create new procedures, which we are sure don't collide with any name, we can do this safely. #+end_quote ** ~borrow~ See [[#distinct-types][Distinct Types]]. * Questions/Doubts ** NEED__TO__UNDERSTAND What are ~ptr~ and ~addr~? CLOSED: [2018-06-27 Wed 14:12] See the ~cast~ example in section [[* Specifying Types]] where those get used. ** DONE Better understand the Nim "do notation" syntax CLOSED: [2018-06-27 Wed 14:11] See the example in [[#first-class-functions]]. Does that notation always have to be on one line? Can it be broken across multiple lines for better readability? See [[#do-notation][Do notation]]. * References - [[https://nim-by-example.github.io][Nim By Example]] - [[https://nim-lang.org/docs/tut1.html][nim-lang.org Tutorial 1]] * Footnotes [fn:2] The manual uses this pragma exchangeably as ~{.noInit.}~ and ~{.noinit.}~ at various places in documentation and Nim core code.. that was confusing. Actually that case of the inner ~I~ does not matter. I will just use ~{.noinit.}~ in this document for consistency. [fn:1] If using Nim 0.18.0 or older, use ~nim --advanced~. * COMMENT Local Variables :ARCHIVE: # Local Variables: # eval: (toggle-truncate-lines 1) # org-hugo-auto-export-on-save: nil # End: