Appendix H: Coming From Another Language
The places Zuri will surprise you are the places it looks most familiar. This is that list.
Everyone
| You expect | Zuri does |
|---|---|
x++ as a statement only | x++ is postfix only, and in an expression it evaluates to the new value. ++x does not parse. |
[] to be falsy | [] and {} are truthy. Use is_empty(). |
finally | There is none. Code after the catch statement runs either way. |
try | The keyword is catch, and it takes the block that might fail: catch { ... } as e { ... }. |
new Thing() | Call the class: Thing(). |
switch/case with fall-through | using/when, first match only, no break. |
a main function | A file’s top level is the program. |
| declaration hoisting | None. A def must appear above the top-level line that calls it. |
| overloading by arity | None. Two defs of one name in one scope is a compile error. |
| reopening a class | class Ext > Target adds methods to an existing class, globally. See Extensions. |
string ordering with < | < is numbers only. Use compare(), which returns -1, 0 or 1. |
x in collection | No membership operator. Use contains(). |
?. and ?? | Neither exists. or covers the common case, with the truthiness caveat above. |
an eval() | There is none, deliberately. See Metaprogramming. |
From Python
- Blocks are braces, not indentation, and every control-flow body takes one.
defdeclares functions andclassdeclares classes, but there is noselfparameter:selfis implicit inside a method and required for every field access.- The constructor is
@new, not__init__. Dunder methods are@-prefixed decorated methods:@add,@lt,@key,@to_json. __eq__is@eq, and!=is always its negation. It runs only when the other side is an object too, sox == nilnever calls it. Without it, instances compare by identity.- No list comprehensions.
map(),filter()andreduce()are methods on the list. len(x)isx.length().str(x)isx.to_string().int(x)isx.int()orx.to_number().- Slicing is
s[a, b], with a comma, nots[a:b]. elifiselse if.- Modules run once and are cached, as in Python. Circular imports behave the same way, and have the same caveat.
if __name__ == '__main__'isif __root__ == __file__.
From JavaScript
varis block-scoped and behaves likelet.constprevents rebinding and is enforced in local scopes.==does no coercion.'1' == 1isfalse. There is no===.- Arrow functions are
@(x) => x * 2ordef(x) => x * 2. The@is the common spelling. - There is no
thisrebinding to worry about.selfis the instance, always. - Objects and dictionaries are the same thing, and a class is not one.
Classes are sealed: you cannot add a property to an instance, though a
class Ext > Targetdeclaration can add a method to the class. nullandundefinedare bothnil.- No
async/awaitand no event loop. Concurrency is isolates: real threads with separate heaps, communicating by copying. JSON.stringifyisjson.encode, andcompactdefaults totrue.- Template literals are
'${expr}', in ordinary single or double quotes.
From Ruby
- No implicit returns. A function without
returnyieldsnil. - No blocks or
yield. Pass an anonymous function. nilis the only nil-like value, andfalseis separate from it.- Methods do not end in
?or!. Predicates are namedis_*and mutation is documented rather than punctuated. eachhands the callback value first, index second.forhands you key first, value second.- There is no
method_missing. Adding methods to an existing class is possible, but through an explicitclass Ext > Targetdeclaration rather than by reopening the class. - Modules are files, not a language construct. There is no
includeorextend.
From Go
- Dynamically typed, with optional annotations on parameters that are checked at every call.
- Errors are raised and caught, not returned. There is no
err != nilpattern. - Isolates are not goroutines. They are OS threads with separate heaps, and values crossing between them are copied. That is the whole concurrency model, and it is why there are no mutexes.
- Channels are bounded queues and behave the way you expect,
select()included. - No interfaces. A parameter typed
Erroraccepts any subclass, and that is the whole of the polymorphism story alongside inheritance. deferhas no equivalent. Close what you opened, on both paths.
From Java or C#
- No static typing, no generics, no interfaces, no packages-as-namespaces. A module is a file.
- Single inheritance, and no
abstractkeyword. A base method that raisesNotImplementedErroris the idiom. public/privateis a leading underscore, enforced at compile time for both class members and module members.- There is no overloading. One name, one method, and a second declaration of either is a compile error.
equals()is@eq, and==calls it.toString()isto_string(), and nothing calls it for you. Whatechoshows for an instance comes from@to_string().
From C
- Numbers are doubles. There is no integer type, and
/never truncates.//is floor division. %keeps the sign of the left operand, as in C.//rounds toward negative infinity, which C’s/does not.- No pointers, no manual memory management. A generational collector owns the heap.
bytesis the buffer type, andstructis how you read and write binary layouts.switchisusing, with no fall-through.
Things That Will Save You an Hour
Interpolation does not call to_string(). '${thing}' is
<instance of Thing>. Call the method. echo shows an instance through
@to_string(), a separate method.
list.sort() mutates and returns; list.reverse() does neither to the
original. That asymmetry is the most common list bug in Zuri code.
A def scopes like a var. At the top level of a file it binds a
module-level name; anywhere else it is a local of the block it is written
in, and it goes away with that block.
import is local by default. If your module imports something and a
third file cannot see it through you, add the @.
A same-directory import is import .sibling, not the full path from
the project root.
A conditional expression breaks across lines either way. ? and :
may each end a line or begin the next, so cond ? on one line and a
line starting with ? or : both parse.