NURL

Common pitfalls

The handful of things worth knowing before the compiler tells you.

The compiler catches and explains every source-level trap. NURL's errors are the first place to look. Do not memorize a list in advance. This page shows the few patterns worth learning up front. You can see the fix more easily when you know the rule.

Calls always need parentheses

nurl_print `hello`     // error: bare identifier as a statement has no effect
( nurl_print `hello` )  // correct

A bare identifier is always a name lookup, never a call. If you write a call without parentheses, it is a dead statement with no effect. The compiler rejects it.

^ is return, not XOR

^ a b     // "return a", then a separate, likely-unintended statement "b"
^^ a b    // XOR of a and b

^ is the unary return operator. ^^ — two carets with no space between them — is binary XOR. A stray space between two carets silently becomes two return statements instead of one XOR; the compiler warns when this shape appears on one line.

Struct parameters are copies by default

NURL copies struct parameters into the callee. If you write to a field inside the callee, you do not change the caller's value. Declare the parameter inout to change the caller's value. See Memory and ownership.

A closure that changes a capture cannot leave its frame

A closure snapshots what it only reads when you create it: later changes to the outer variable do not show inside the closure. A : ~ binding the closure assigns, or hands to an inout parameter, it captures by reference instead, so the write lands in the caller's binding — and such a closure cannot be returned, stored in a container or handed to a thread. State a closure changes after its frame is gone belongs in an Atomic, a Locked or an Rc. See Memory and ownership and docs/MEMORY.md §2.3.

Rc cannot cross a thread boundary

Capturing an Rc (a non-atomic reference count) in a thread_spawn or spawn closure is a compile error. If two threads update the count at the same time, the behavior is undefined. Use Arc for anything shared across threads or fibers. See Concurrency.

Single-letter type keywords cannot be inferred variable names

i u f b s v are type keywords, not ordinary identifiers. : n 0 infers n's type from 0. If you name the binding i the same way, the compiler rejects it. i is a reserved type name. Give the binding an explicit type annotation: : i n 0 works as a type annotation on a differently-named variable. But a binding named i must have a different spelling.

Import paths: beside the importer first, then the working directory

$ "path" is tried relative to the file that contains the $ first, then the compiler's current working directory, then $NURL_STDLIB. Stdlib imports (stdlib/core/vec.nu) resolve through the last two. A package's own modules import each other by their path from the importing file — store.nu from src/service.nu, not src/store.nu: the src/ form only resolves while the working directory is the package root, so it breaks once the package is installed under a consumer's deps/<pkg>/.

Next

Last updated on

On this page