Blog › ICP guides
Neko developer on retainer: $hash factory pattern, $hset/$hget operations, Neko object model, and NekoVM programming on monthly retainer
November 10, 2026 · ~15 min read
A Neko program maintaining a registration cache was producing five wrong retrievals per run. The program stored user records with $hset(cache, "user_1", record), populating a hash table referenced by the variable cache. The lookup function used $hget($hash(), key) to retrieve entries — but $hash() is a factory function that creates a brand-new empty hash table on every call, not a reference to an existing table. Every invocation of the lookup function created a fresh empty table and then tried to read from it, returning $null for every key regardless of what had been stored. Five lookup calls per run hit this path, producing five wrong null retrievals. The Neko developer on retainer diagnosed the factory-vs-reference confusion: in Neko, $hash() is like new HashMap() in Java — you call it once at creation time, store the result, and thereafter pass the stored reference wherever hash table operations are needed. The fix restructured the lookup function to reference the pre-allocated cache variable: $hget(cache, key). Wrong retrievals per run: 5 → 0.
The work log entry read “fixed registration cache lookup, 11h.” It names the symptom and duration. It cannot explain to a client why Neko’s $hash() creates a new empty hash table on each call (Neko’s entire built-in function library follows the convention that $ prefix functions are constructors and operations rather than mutable singletons; $hash() constructs a new hash table value — a first-class value in Neko’s type system that can be stored in variables, passed to functions, and returned from functions — while the operations $hset, $hget, $hmem, $hremove, and $hiter all take an existing hash table value as their first argument; calling $hash() to retrieve instead of calling it once to create is a misreading of the factory-vs-accessor pattern that Neko’s built-in library consistently applies), why the fix required auditing every $hget call site to verify the first argument is a stored variable rather than a factory call (a cache that is never populated from lookup’s perspective produces $null for every key, but a $null result from $hget is also the signal for a legitimately absent key; distinguishing legitimate absence from factory-misuse absence requires tracing the hash table reference back to its $hset population site), or why null safety around $hget returns required separate remediation in three other lookup paths (code that directly called a method on the $hget return value without first checking for $null caused runtime null dereference crashes whenever the key was legitimately absent; the factory bug and the null safety bug appeared together in the same retrieval function but are independent failure modes). The 11 hours of $hash factory call-site audit across the full codebase, null guard design for all $hget returns, and cache reference propagation verification are not visible in the diff beyond corrected $hget argument expressions.
Neko hash tables: $hash factory, $hset, $hget, $hmem, $hremove, $hiter
Neko’s hash table is the language’s built-in key-value map. $hash() creates a new empty hash table and returns it as a first-class value; the result must be stored in a variable for subsequent use. $hset(h, key, value) inserts or updates the entry for key in hash table h with value; if the key already exists, its value is replaced. The key can be any Neko value but integer and string keys are the most common in practice. $hget(h, key) retrieves the value associated with key in hash table h; it returns $null if the key is not present. Every $hget call site must handle the $null return: code that dereferences or calls a method on the $hget result without a prior $null check will crash with a runtime null dereference error on any cache miss. The correct null-safety pattern: store the $hget result in a variable, compare it to $null with the equality operator, and branch before using it.
$hmem(h, key) tests membership: it returns $true if key exists in hash table h, and $false otherwise. $hmem is useful when the presence of a key is the information of interest and the associated value is either not needed or may legitimately be $null itself (in which case checking the $hget return for $null cannot distinguish presence-with-null-value from absence). $hremove(h, key) deletes the entry for key from hash table h; if the key is not present, the call is a no-op. $hiter(h, f) iterates over all entries in hash table h, calling the function f with two arguments (key and value) for each entry. The iteration order is not guaranteed; hash table entries are visited in an implementation-defined order. $hiter is used for full-table export, serialization, or aggregate operations (count all entries matching a predicate, collect all keys, etc.). The function f passed to $hiter can use $hset/$hremove on h during iteration, but the behavior when modifying the table being iterated is implementation-defined and generally unsafe.
Hash table key design in Neko: integer keys and string keys are the two primary patterns. Integer keys produce fast hash lookup based on the integer value directly. String keys produce lookup by string content; two string values with the same character sequence compare as equal keys. Object or function values as keys compare by identity (reference equality), not by content. The key equality semantics matter for cache design: a cache keyed on string user IDs or string path components uses string content equality and will find entries regardless of which string object was used to store them; a cache keyed on object identity will not find an entry even if the key object has the same field values as the stored key, because identity comparison requires the same object reference. Retainer work on hash table design often involves diagnosing key equality mismatch: a string key stored with one string literal and retrieved with a dynamically constructed string with the same characters will succeed in Neko (string equality by content), but a developer switching from string keys to object keys for a performance optimization loses the content-equality property.
Neko value model: $null, $true/$false, int, float, string, function, array, object, hash, abstract
Neko is dynamically typed: every variable can hold any of the ten value types, and type checking happens at runtime. The ten Neko value types are: null (the single $null value, representing absence); bool ($true and $false, the two boolean constants); int (31-bit signed integer on 32-bit platforms, 63-bit on 64-bit platforms); float (64-bit IEEE 754 double); string (byte sequence, mutable in Neko — unlike many scripting languages, Neko strings are not immutable); function (a callable value created with the function(arg1, ...) expr syntax); array (an ordered sequence of values with integer indices); object (a hash map of field symbol to value, prototype-based); hash (the general hash table created with $hash()); and abstract (a type-tagged wrapper around any value, created with $abstract(name, value)). The built-in $typeof(v) function returns an integer code identifying the type: 0=null, 1=int, 2=float, 3=bool, 4=string, 5=object, 6=array, 7=function, 8=abstract (note: hash is an abstract type internally; $hash() returns an abstract value with the internal hash tag).
Integer arithmetic in Neko: standard +, -, *, / operators work on int values; / is integer division (truncating toward zero) when both operands are int. The % operator is the integer modulo. Bitwise operators: & (AND), | (OR), ^ (XOR), ~ (NOT), << (left shift), >> (signed right shift), >>> (unsigned right shift). Comparison operators: ==, !=, <, >, <=, >= produce $true or $false. The == operator tests value equality for primitives (int, float, bool, string) and reference equality for objects and arrays. Float arithmetic: when either operand is a float, Neko coerces the other to float and produces a float result. The built-in $int(v) converts a float or string to int; $float(v) converts an int or string to float. String operations: $string(v) converts any value to its string representation; string concatenation uses the + operator in Neko; $size(s) returns the byte length of a string; $sub(s, pos, len) returns a substring; $sget(s, i) returns the byte at position i as an integer; $sset(s, i, v) sets the byte at position i (Neko strings are mutable at the byte level). $compare(a, b) returns a negative integer if a < b, 0 if equal, positive if a > b — useful for sorting and ordering operations.
The $null value and null safety: $null is Neko’s universal absence marker. Many $-prefix operations that could fail to produce a meaningful result return $null: $hget on a missing key, $aget on an out-of-bounds index, $get on a missing field, and $loadmodule when a module cannot be found. Code that assumes these operations always return a non-null value and immediately dereferences or calls a method on the return will crash at runtime. The retainer work of null safety audit: for every $hget, $get, $aget, and $loadmodule call site in the codebase, verify that either the result is compared to $null before use, or the absence path is structurally impossible (the key was just set with $hset in the same code block, making a missing-key return impossible). Boolean semantics: in Neko conditional contexts (if, while, etc.), only $false and $null are false; every other value — including 0, empty string, and empty array — is truthy. This differs from JavaScript and Python; a Neko developer writing if(count) { ... } where count is an int intends to check that count is non-null-and-non-false, but the condition is truthy for 0 as well as for any positive integer.
Neko object model: $new, $objfields, $field, $get, $set, prototype delegation
Neko objects are prototype-based: every object can optionally have a prototype object that serves as a fallback for field lookups. $new($null) creates a new empty object with no prototype. $new(proto) creates a new empty object whose prototype is proto; field lookups on the new object fall through to proto if the field is not found on the object itself. This prototype chain is Neko’s mechanism for single-inheritance delegation: a “class” is an object used as a shared prototype, and instances are new objects created with $new(classObject). Fields can be added to a prototype after instances are created; all instances that do not have the field locally will see the prototype’s field through delegation.
Field access in Neko uses symbol-based addressing rather than string-based addressing. $field("name") interns the string "name" and returns a field symbol — an interned integer identifier. Field symbols are reused across all objects that have a field with the same name, making field access faster than a string hash lookup on each access. The pattern: declare field symbols once (at module initialization time or as module-level variables) with var fName = $field("name");, then use the symbol everywhere: $set(obj, fName, value) to write, $get(obj, fName) to read. Calling $field inside a hot loop is a common performance mistake: each call interns the string, but the interning operation has overhead; hoisting the $field call to module scope eliminates this overhead. $get(obj, f) returns the value of field f in object obj; if the field is not present on obj it walks the prototype chain; if not found anywhere, returns $null. $set(obj, f, value) sets the value of field f on obj directly (never on the prototype). $objfields(obj) returns an array of all field symbols defined directly on obj (not including prototype fields), useful for introspection and serialization.
Function values in Neko: function(a, b) expr creates a function value that closes over the surrounding scope. Functions are first-class: they can be stored in variables, set as object fields ($set(obj, fMethod, myFunc)), and passed to $hiter/$call. $call(f, args) calls function f with the array args as positional arguments; the function receives array elements as its named parameters in order. $nargs(f) returns the arity of function f: the number of arguments it expects. A function declared with a fixed argument list returns its count; variadic functions (declared with a rest parameter) return -1. The $call mechanism is used for dynamic dispatch: when a field on an object holds a function value, code can read the field with $get and then call it with $call, passing an arguments array. This is Neko’s manual equivalent of method dispatch in object-oriented languages. Retainer work on object model design includes designing the method-dispatch pattern: storing function values as prototype fields (so all instances share the method through prototype delegation), calling instance methods with the instance as the first argument (Neko has no automatic this binding; the instance must be passed explicitly), and designing constructor functions that initialize new instances with $new and $set for all required fields.
Neko modules and NekoVM: $loader, $loadmodule, $exports, $abstract types, and Haxe integration
Neko programs are organized into modules: source files compiled to .n bytecode files by the nekoc compiler. $loader is the platform-provided module loader object: it has methods for finding, loading, and caching module bytecode. $loadmodule(name, loader) loads a module by name using the given loader; it returns an object containing the module’s exports. The name is a module path string: either a relative path (resolved against the loader’s search path) or an absolute path. Modules export their public interface through $exports: code in a module assigns values to fields of $exports to make them available to importing modules. The canonical pattern: $exports.hash_lookup = function(cache, key) { return $hget(cache, key); }; at module top-level makes hash_lookup available in any module that loads this one and reads the returned exports object. Module loading is cached by the loader: loading the same module name twice returns the same exports object; the module code executes only once on first load.
Abstract types in Neko: $abstract(name, value) wraps value in a named type tag. The result is a Neko abstract value that reports as type abstract (type code 8 from $typeof), with the tag name identifying the abstraction. $isabstract(v, name) tests whether v is an abstract value with the tag name matching name; it returns $true or $false. $abstractvalue(v) unwraps the inner value from an abstract wrapper. Abstract types are Neko’s mechanism for creating opaque types in a dynamically typed language: a library can create values tagged with a library-specific name (e.g., "my_lib.connection") and check for that tag at every entry point, refusing to operate on values that do not carry the expected tag. This provides a form of type safety without a static type system. The Neko standard library uses abstract types extensively: the hash table itself ($hash() returns an abstract value tagged internally), file handles, regular expression objects, and socket descriptors are all Neko abstract values that the runtime checks for the correct tag before performing operations.
Neko as a Haxe compilation target: Neko was created by Nicolas Cannasse as part of the Haxe ecosystem. Haxe’s neko target compiles Haxe source to Neko bytecode, which runs on NekoVM. This made Neko the standard server-side and tooling runtime for early Haxe projects before the JavaScript and HashLink targets matured. Retainer work on Neko in Haxe-adjacent contexts involves diagnosing mismatches between Haxe’s type system guarantees and Neko’s runtime behavior: Haxe’s type system enforces null safety for non-nullable types at the Haxe level, but Neko code can bypass this through direct $get access on objects that the Haxe compiler assumes are fully initialized. neko.Lib.load(name, funcName, arity) is the Haxe-level function for loading native C extensions compiled to Neko’s C API; retainer work includes diagnosing arity mismatches (the arity argument must match the C function’s parameter count exactly; arity mismatch causes silent wrong-argument-count errors or crashes rather than a compiler error). HashLink has replaced NekoVM as the primary Haxe server-side and desktop target for new projects, but existing Neko deployments in build toolchains and CMS systems (nekotools, Haxe compiler itself, HIDE IDE) continue to require Neko retainer expertise.
How HourTab tracks Neko developer retainer hours
Neko retainer work shares the invisible-work problem common to all language engineering retainers, compounded by the fact that Neko’s most common retainer tasks — $hash factory call-site audit, $null safety remediation, $field symbol hoisting, prototype chain design — produce diffs whose surface area is small relative to the diagnostic work. A $hash factory fix is a diff with one argument changed in one $hget call; the value is elimination of all wrong retrievals from the cache lookup path, correct cache reference propagation through every code path that reads from the cache, and a correct understanding of Neko’s factory-vs-accessor distinction that prevents the same bug from recurring in the next cache-like data structure the team builds. A $null safety audit across all $hget, $get, and $aget call sites that adds $null guards before every dereference is a diff with dozens of small changes scattered across the codebase; the value is elimination of all runtime null dereference crashes on cache miss paths, a robust handling of legitimate absent-key conditions, and a clear distinction in the code between “key known to be present” paths and “key may be absent” paths. A $field hoisting optimization that moves $field(name) calls from inside hot loops to module-level variables is a diff with a few declarations added at the top of each function; the value is measurable throughput improvement on all paths that repeatedly access the same object field.
HourTab gives Neko developers a public retainer-hours URL they send to clients — typically game engine tooling groups using Neko as a scripting layer, Haxe ecosystem teams maintaining legacy neko-target codebases, and build pipeline engineers running nekotools for Haxe compilation — at the start of an engagement. For Neko retainers, each work log entry should name the mechanism ($hash factory call-site audit; $hset/$hget/$hmem/$hremove hash table operation; $hiter traversal callback design; $null safety guard addition; $new prototype chain design; $field symbol interning and hoisting; $get/$set field access; $objfields enumeration; $call/$nargs dispatch; $amake/$asize/$aget/$aset array design; $loadmodule dependency resolution; $exports interface design; $abstract/$isabstract type tagging; nekoc compilation), the specific variable and function name involved, the factory-vs-accessor bug or null safety failure, and the before/after metric. Neko retainers are often compared to Haxe developer retainers for Haxe ecosystem work, and to Lua developer retainers for lightweight scripting VM engineering. HourTab’s work log makes the $hash factory audit, null safety remediation, and prototype chain design visible to clients who would otherwise see only the symptom — wrong retrievals or runtime crashes — and not understand why the fix required understanding Neko’s factory-vs-accessor pattern and the $null return semantics of every hash and object access function.
Track Neko developer retainer hours without the status emails
HourTab gives Neko developers a public URL per client retainer. One link, no login, live burn-down. Your clients stop asking “how many hours do I have left?” and your work log becomes the proof of value that gets the retainer renewed.
See HourTab pricing →FAQ: Neko developer retainers
What does a Neko developer on retainer typically do?
A Neko developer on monthly retainer covers four principal service areas: hash table design ($hash factory call-site audit; $hset/$hget/$hmem/$hremove operational correctness; $hiter traversal callback design; $null return handling from $hget on missing keys; cache reference propagation); object model design ($new(proto) prototype chain; $field(name) interned symbol authorship; $get/$set field access; $objfields dynamic field enumeration; $call/$nargs function dispatch; $null guard design before all dereferences); array programming ($amake/$asize/$aget/$aset; $array literals; index bounds analysis; array-backed collections); and module and abstract type system work ($loadmodule name resolution; $loader platform dependency; $exports interface design; $abstract/$isabstract type-tag safety patterns; nekoc compilation pipeline).
What Neko work is most commonly underlogged in a retainer?
$hash factory pattern repair (cache populated with $hset but lookup called $hget($hash(), key) creating new empty table each retrieval; 5 wrong retrievals/run; restructured to $hget(cache, key) using stored variable; wrong retrievals: 5/run → 0; 12–20 hrs invisible in $hash/$hget call-site audit and cache reference propagation design), null dereference audit on $hget returns ($hget returns $null on missing key; immediate dereference of return value caused 7 crashes/day; added $null check before each $hget return use; crashes: 7/day → 0; 9–16 hrs invisible in null safety audit), and $field symbol hoisting ($field called inside hot loops creating new interned symbol each iteration; hoisted to module scope; 8–14 hrs invisible in $field call frequency analysis and performance optimization design).
What are typical Neko developer retainer rates?
Entry-level Neko developers (1–2 years, basic $hash/$hset/$hget, simple $new object creation, $asize/$aget/$aset array access) bill at $60–$105/hr. Mid-level Neko engineers (2–4 years, $null safety patterns, $field symbol interning, $hiter traversal, $loadmodule module system, $abstract type tagging) bill at $95–$175/hr. Senior Neko architects (4–8 years, full NekoVM bytecode pipeline, Neko C embedding, $loader platform customization, complex prototype chains, Neko standard library internals) bill at $145–$260/hr. Monthly retainer ranges: $1,600–$4,500/mo advisory (15–25 hrs), $6,500–$16,000/mo for full NekoVM platform engagements.
What should a Neko developer retainer agreement include?
A Neko developer retainer agreement should specify: hash table scope ($hash factory call-site audit; $hset/$hget/$hmem/$hremove operational correctness; $hiter traversal callback; $null return handling; cache reference propagation); object model scope ($new prototype chain design; $field interned symbol authorship; $get/$set field access; $objfields enumeration; $call/$nargs dispatch; $null guard design); array scope ($amake/$asize/$aget/$aset; $array literals; index bounds analysis); module and abstract type scope if applicable ($loadmodule path resolution; $loader dependency; $exports interface; $abstract/$isabstract type tagging; nekoc compilation); and hour logging format (operation type; before/after error metric; Neko version and platform; whether fix was $hash() call-site correction, $null guard addition, $field hoisting, or $loadmodule path fix).
How should Neko developer retainer hours be logged?
Log each Neko retainer session with: advisory category ($hash factory call-site audit; $hset/$hget/$hmem/$hremove hash table design; $hiter traversal callback; $null safety on $hget returns; $new prototype chain; $field symbol interning and hoisting; $get/$set field access; $objfields enumeration; $call/$nargs dispatch; $amake/$asize/$aget/$aset array; $loadmodule dependency resolution; $exports interface; $abstract/$isabstract type tagging; nekoc/neko build), the specific function and variable name involved in the bug (cache hash table populated with $hset; lookup called $hget($hash(), key) creating new empty table; 5 wrong retrievals/run; restructured to $hget(cache, key); wrong retrievals: 5/run → 0), and the before/after observable metric. Include Neko version and execution context (NekoVM standalone, embedded via neko C API, Haxe neko target), and whether the fix required $hash() call-site correction, $null guard addition, $field symbol hoisting, prototype chain restructuring, or $loadmodule path resolution.