-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Lesser known Nim features
# `a` can accept values 0, 1, 2, 3
var a: 0 .. 3 = 2 # ok
a = 6 # compile error
a = 0; a.inc 10 # runtime error OverflowDefect
type
Test = object
port: 1 .. 65535
let t = Test(port: 70000) # error
Both of these ways are actually used with templates/macros, but not a lot of people realise that you can use them with normal procs (because in Nim a code block evaluates to the last expression in it if that block needs to return a value)
# first example
proc tata(a, b: int) =
echo a + b
tata(5): 3 # 8
tata():
5
do:
3
# another way of writing the second example
tata do:
5
do:
3
See do notation
type
Dollar = distinct int
var a = 5.Dollar
int(a) = 3
Documented in https://nim-lang.org/docs/manual.html#type-relations-convertible-relation (last sentence in that section)
Since enums are Ordinal types, they can be used for indexing arrays (this example might not be the best since you can define default string representation for enum values, but might be useful in other cases):
type
Values = enum
String, Int, Float, Other
# Here we explicitly specify indexes
const myArr = [
String: "some string",
Int: "some int",
Float: "some float",
Other: "something else"
]
echo myArr[Int]
# Or like that
const other: array[Values, string] = [
"some string", "some int",
"some float", "something else"
]
echo other[Float]
# Same as if unlikely(5 > 3)
# or if (unlikely) 5 > 3
if(unlikely) 5 > 3:
echo "not possible"
else:
echo "true"
(echo)5 # Same as echo 5 or echo(5)
You can use try / except as an expression:
import strutils
let mystr = "hello"
let myint = try:
mystr.parseInt()
except ValueError:
-1
echo myint
Documented at https://nim-lang.org/docs/manual.html#exception-handling-try-expression
proc foo(a: int) =
echo "argument a: ", a
proc foo(b: int) =
echo "argument b: ", b
foo(a=1)
foo(b=2)
proc square(inSeq: seq[float]): seq[float] = (
result = newSeq[float](len(inSeq));
for i, v in inSeq: (
result[i] = v * v;
)
)
echo square(@[1.0, 2, 3])
See https://nim-lang.org/docs/manual.html#statements-and-expressions-statement-list-expression
proc mytest(a: string, b = "hello " & $(len(a) + 5)) =
echo b
mytest("hello world")
That example is a bit stupid, but it gives you an idea of what is possible :)
A more complex example which also makes use of the static[int]
argument:
type
Lexer = object
buf: seq[string]
idx: int
func `[]`(l: Lexer, idx: static[int]): string =
## Get token relative to current position - only works with literal integers
l.buf[l.idx + idx]
func `[]`(l: Lexer, idx: int = l.idx): string =
## Get current token by default but can get one at specific index if parameter is provided
l.buf[idx]
func next(l: var Lexer) = inc l.idx
var buf = Lexer(buf: @["hello", "world", "nice"])
echo buf[] # Get current token
echo buf[+2] # Check to next tokens
buf.next() # Move to next token
echo buf[-1] # Prevous
for i in 0 ..< buf.buf.len:
echo buf[i] # Iterate over all tokens
Or the last, craziest example:
proc tmp(a = 12, b = a + 2, c = ((
block:
var buf: string
for i in 0 .. 2:
buf &= " * " & $i
buf
))
) = echo b, c
tmp(b = 122)
For modules where some kind of common state object is being passed around as argument to all procs it quickly becomes annoying to write it over and over again, especially if it has name that is longer than several characters. One possible workaround is to use type alias like type Ps = ProcessingState
or try to use shorter names, but using
statement is a better solution.
You can override type of the pc
parameter - using
only provides the default fallback type.
type
ProcessingContext = object
env: seq[string]
using
pc: ProcessingContext
# Argument type is inferred from `using`
proc doThings1(pc; args: seq[int]) =
# ^
# Note that semicolon (`;`) should be used to separate arguments
# with implicit types (declared with `using`) declaration
static: echo "1 ", typeof pc
echo pc.env
# Written explicitly
proc doThings2(pc: ProcessingContext, args: seq[int]) =
# ^ ^
# | If type is written explicitly you can use
# | regular comma (`,`)
# Can set parameter type explicitly. Not needed with `using` but
# still possible.
static: echo "2 ", typeof pc
echo pc.env
# Override argument type
proc doThings3(pc: int, args: seq[int]) =
static: echo "3 ", typeof pc
Output:
1 ProcessingContext
2 ProcessingContext
3 int
Code speaks for itself:
type
MyArr = array[8, array[8, int]]
proc `[]`(b: MyArr, r, c: int): int =
b[r][c]
proc `[]=`(b: var MyArr, r, c: int, val: int) =
b[r][c] = val
var b: MyArr
b[0, 3] = 5
echo b[0, 3]
Iterate over collection of static
values:
import strscans
for ch in fields(("a", "hay$i")):
var i: int
if scanf("hay12", ch, i):
echo i
Regular iteration over ["a", "hay$i"]
would make ch
just a string
,
but with fields
iterator loop is unrolled, so ch
is a static[string]
,
making it possible to use with scanf()
which accepts compile-time
constant as it's second argument.
Intro
Getting Started
- Install
- Docs
- Curated Packages
- Editor Support
- Unofficial FAQ
- Nim for C programmers
- Nim for Python programmers
- Nim for TypeScript programmers
- Nim for D programmers
- Nim for Java programmers
- Nim for Haskell programmers
Developing
- Build
- Contribute
- Creating a release
- Compiler module reference
- Consts defined by the compiler
- Debugging the compiler
- GitHub Actions/Travis CI/Circle CI/Appveyor
- GitLab CI setup
- Standard library and the JavaScript backend
Misc