Nim cheat sheet
Write to stdout:
echo "Hello, world!"
Build:
nim c main.nim
Compile and run:
nim c -r main.nim
HINT: Add the --verbosity:0
flag to turn off extra info printed by the compiler.
const
variables are immutable and computed at compile time.
const x = 5
let
variables are immutable and computed at run time.
let x = 5
var
variables are mutable.
var x = 5
Types can be inferred or written explicitly. Both of these lines are valid:
var x = 5
var x: int = 5
Here are the most salient primitive types:
float
int
bool
string
char
nil
Enums:
type
Directions = enum
North, South, East, West
echo Directions.North == Directions.South
# false
Arrays have a fixed length:
var x: array[5, int] = [100, 200, 300, 400, 500]
Sequences do not have a fixed length:
var x: seq[int] = @[]
x.add(100)
x.add(200)
x.add(300)
x.add(400)
x.add(500)
Mapping and filtering of sequences (and maybe arrays?) can be enabled by importing the sequtils
module from the standard library:
import std/sequtils
var x: seq[int] = @[1, 2, 3, 4, 5]
var y: seq[int] = x.map(proc (v: int): int = v * 2)
echo y
var z: seq[int] = x.filter(proc (v: int): bool = v mod 2 == 0)
echo z
There's apparently also a lambda function syntax that uses the do
keyword:
var y: seq[int] = x.map do (v: int) -> int: v * 2
Personally, though, I think my favorite is the JS-like fat-arrow notation, though it requires importing the standard library's sugar
module:
import std/sugar
var y: seq[int] = x.map(v => v * 2)
I'm not quite sure yet how to indicate parameter and return types in this notation, though. Hopefully, it won't be very hard to figure out!
Objects in Nim are like structs in other C-languages: they can't have methods; they can only have properties. However, methods, inheritance, and other OOP features are available. See the OOP section below for more info.
type
Person = object
name: string
age: int
var alice = Person(name: "Alice", age: 23)
Check if a value is of a certain type using the is
keyword:
echo 234 is int
echo "foo" is string
echo alice is Person
Conditionals:
var x = 0.25
if x < 0:
echo "Less than 0!"
elif x > 1:
echo "Greater than 1!"
else:
echo "Between 0 and 1!"
while
loops:
var i = 0
while i < 10:
i += 1
echo i
for
loops:
var fibs: seq[int] = @[1, 1, 2, 3, 5, 8, 13, 21]
for v in fibs:
echo v
for i, v in fibs:
echo v & " is the value at index " & i
for i in 0..100:
echo i
NOTE: The ..
operator is inclusive on both ends! So, for example, 0..100
represents the range [0, 100] (inclusive).
Exception handling:
proc somethingStupid() =
raise newException(Exception, "Nope!")
var failed = false
try:
somethingStupid()
except Exception as e:
echo "ERROR: " & e.msg
failed = true
echo failed
proc fib(n: int): int =
if n < 2:
return 1
return fib(n - 1) + fib(n - 2)
for i in 0..10:
echo fib(i)
Procedures can be overloaded:
proc add(a: float, b: float): float =
return a + b
proc add(a: int, b: int): int =
return a + b
proc add(a: string, b: string): string =
return a & b
echo add(1.2, 3.4)
echo add(234, 567)
echo add("foo", "bar")
Procedures can also be generic:
proc reverse[T](x: seq[T]): seq[T] =
var temp: seq[T] = @[]
for i in 0..len(x)-1:
temp.add(x[len(x) - 1 - i])
return temp
echo reverse(@["a", "b", "c"])
echo reverse(@[1, 2, 3])
Procedures can be used to create and/or overload operators:
proc `@`(a: float, b: float): float =
return a * b
echo 234 * 567
echo 234 @ 567
proc `+`(a: string, b: string): string =
return a & b
echo "foo" + "bar"
NOTE: Operator "names" must be wrapped in backticks and (as far as I can tell) can only contain symbols.
The keyword varargs
can be used as a placeholder for to mean "all of the arguments passed into the procedure":
proc printAllTheThings(things: varargs[string]) =
for thing in things:
echo thing
printAllTheThings("a", "b", "c")
printAllTheThings($1, $2, $3)
Multiline strings:
var x = """
Hello, world!
This is a multiline string.
"""
Concatenate strings with the &
operator:
echo "foo" & "bar"
Convert values to strings using the $
operator:
echo 234 & " is a " & typeof 234
echo $234 & " is a " & typeof $234
For most interesting string-related tasks, we'll need to import the standard library's strutils
module:
import std/strutils
(The remainder of this section will assume that the strutils
module has been imported.)
Check for the existence of a substring within a string:
echo "foo" in "foolish"
echo "foo" in "nope"
echo contains("foolish", "foo")
echo contains("nope", "foo")
Use some preexisting character sets:
echo Digits
echo HexDigits
Interpolate / format strings:
echo "My name is $1, and I'm $2 years old!" % ["Josh", "37"]
echo "My name is $#, and I'm $# years old!" % ["Josh", "37"]
Count occurrences of a substring:
echo count("fooooooood", "o")
echo count("foofoofoofoofoo", "foo")
Indent / dedent / unindent text:
var html = """
<html>
<head>
...
</head>
<body>
...
</body>
</html>
"""
echo "====="
echo indent(html, 8)
# =====
# <html>
# <head>
# ...
# </head>
# <body>
# ...
# </body>
# </html>
echo "====="
echo dedent(html)
# =====
# <html>
# <head>
# ...
# </head>
# <body>
# ...
# </body>
# </html>
echo "====="
echo unindent(html)
# =====
# <html>
# <head>
# ...
# </head>
# <body>
# ...
# </body>
# </html>
Check for substrings at the beginning or end of a string:
echo "Hello, world!".startsWith("He")
echo "Hello, world!".startsWith("d!")
echo "Hello, world!".endsWith("He")
echo "Hello, world!".endsWith("d!")
Convert to and from other encodings:
echo fromBin[int]("1010")
echo 10.toBin(4)
echo fromHex[int]("ababab")
echo 11250603.toHex(6)
Join strings:
echo join(["A", "B", "C"], " => ")
Replace substrings:
var food = "food"
while "o" in food:
food = food.replace("o", "-")
echo food
NOTE: There's a multiReplace
function in std/strutils
, but I couldn't figure out the syntax. The compiler didn't seem to like any of the ways I wrote it. 🤷
Parse numbers:
var flote = parseFloat("234.567")
var ent = parseInt("13579")
echo flote, " ", typeof flote
echo ent, " ", typeof ent
Repeat strings:
echo "foo".repeat(5)
Remove leading or trailing whitespace:
echo " hey ".strip()
Change case:
echo "Foo".toLower()
echo "Foo".toUpper()
Create a type:
type Person = object
name: string
age: int
Create an instance:
var alice = Person(name: "Alice", age: 23)
Create methods for the type:
proc sayHi(self: Person) =
echo "Hi! My name is $#!" % [self.name]
NOTE: Using self
as the variable name for a type instance isn't required; it's just the convention.
Call the methods using a variety of syntaxes:
alice.sayHi()
alice.sayHi
sayHi(alice)
Note that this pattern works for non-object types as well:
proc dubble(x: float): float =
return x * 2
echo dubble(234.567)
echo 234.567.dubble()
echo 234.567.dubble
Importantly, values passed into procedures are immutable. This would fail:
proc changeNameToBob(self: Person) =
self.name = "Bob"
changeNameToBob(alice)
echo alice.name
However, arguments can be marked as mutable using the var
keyword in the function signature:
proc changeNameToBob(self: var Person) =
self.name = "Bob"
changeNameToBob(alice)
echo alice.name
To create inheritance, the base type must inherit from RootObj
:
type Person = ref object of RootObj
name: string
age: int
Then it can be sub-typed:
type Employee = ref object of Person
position: string
Use the is
keyword to check if a value is of a particular type:
var dwight = Employee(name: "Dwight", age: 40, position: "Assistant [to the] Manager")
echo dwight is Person
echo dwight is Employee
To mark a variable for export from a file, use an asterisk:
# helpers.nim
proc add*(a: int, b: int): int =
return a + b
Import particular items from a file:
from helpers import add
And rename the imported item, if you like:
from helpers import add as addInts
Or import the entire file:
import helpers
It's also possible to include the contents of a file using the include
keyword:
include helpers
A package (as opposed to a file or module) is a directory with a identifier.nimble
file at its root. If the name of the package is "coolstuff", then the directory should contain a coolstuff.nimble
file. (As far as I can tell, this file doesn't actually need to contain anything; it just needs to exist.)
Read an entire file:
var raw = readFile("myfile.txt")
Read a file one line at a time:
var f = open("myfile.txt")
var lines: seq[string] = @[]
var isStillReading = true
while isStillReading:
try:
lines.add(f.readLine())
except:
isStillReading = false
echo lines
f.close()
NOTE: Don't forget to close
the file when you're done with it (as in the last line of the example above)!
Write an entire file all at once:
writeFile("myfile.txt", "Here is the new content of the file!")
Write to a file one line at a time:
var f = open("integers.txt", fmWrite)
for i in 0..100:
f.writeLine($i)
f.close()
NOTE: Note the use of the fmWrite
constant to enable writing!
Here's the contents of a file called numbers.json
:
[123, 456, 789]
To import the data contained in the file, we need to import the json
module from the standard library and then use the parseJson
procedure:
import std/json
var raw = readFile("numbers.json")
var values: seq[int] = @[]
for v in raw:
values.add(v.getInt())
echo values
Note that values aren't automatically converted into their usual types; instead, it's necessary to call each node's getInt
, getFloat
, getString
, or getBool
methods in order to extract a value.
Converting data to JSON involves using the %*
operator and then converting the result into a string using the $
operator:
var values: seq[int] = @[123, 456, 789]
var raw = $(%* values)
writeFile("values.json", raw)