Clause
← All documentation

Differences from Erlang/OTP

Observable behavior where Clause knowingly differs from the pinned OTP 29 (maint-29, see otp-reference.md). Each entry links to the contract that owns it. Remove an entry once the difference is fixed. Features not implemented yet (reported as notimpl) are not listed here; see features.md and the plan.

Maps

DifferenceOTPClauseOwner
Map generator order (K := V <- M)Flat maps (up to 32 keys) iterate in key order, but atom keys in atom-table order, which varies between VM runs; larger maps in hash orderAlways canonical key order (term order)patterns
bad_generators payload of a map generator in a zip groupIts iterator: {K, V, Next} chain ending in none, in OTP's orderThe same chain, built in canonical key orderpatterns
Map printing (erlang:display/1, ~w, ~p)Internal layout order (atom-table order for atom keys, hash order above 32 keys)Map-key order, as OTP's ~kwterms
Native record printing (erlang:display/1)Fields in atom-table index orderDefinition order, as ~wnative records
== between native records whose fields differ only as integer/floatThe compiler may fold it to =:= (false)Numeric comparison (true)native records
Local fun printing (#Fun<M.Index.Uniq>)Index from the compiler's lambda numbering, Uniq a hash of the module codeIndex of the module's local funs in source order, Uniq always 0funs
Order of two local funs of one moduleBy OTP's indexBy source-order index, so funs of different functions can order differentlyfuns
Anonymous fun names in stack traces (-f/1-fun-N-)N from the compiler's numberingN counts the funs of f/1 in source orderfuns
An anonymous fun in a record field defaultEach construction site expands its own copy: funs from two sites are unequalOne fun for the default: funs from any construction site are equalfuns
Captured value order of funs created inside comprehensionsFree variables of the comprehension's generated functionDefinition order in the enclosing function; visible only when comparing two such funsfuns

Pids, references and processes

DifferenceOTPClauseOwner
Pid numbersThe first user process is about <0.80.0>; numbers are reused after the pid table wrapsThe first process is <0.1.0>; numbers come from one sequence and are never reusedterms
Code after spawn(Fun) of a fun of another arityThe compiler's type analysis may treat the code after the call as unreachable and drop it, so the caller returns the pid at onceThe caller goes on; only the new process fails with {badarity, {Fun, []}}processes
Formatting a large term (io:format/1,2)Runs in Erlang code (io_lib) and the group leader, so the process can be preempted while formattingRuns to completion in one builtin call; other processes waitbuiltins
process_flag/2 flagstrap_exit, priority, message_queue_data, min_heap_size and othersOnly trap_exit; every other flag raises badargprocesses
Error report order of processes crashing at the same timeLogger orderThe order their workers finish themprocesses
Signals to a process running on another schedulerQueued in its signal queue and handled later, while the sender goes onThe sender waits until the target's time slice ends, then acts at once; observable only as timingprocesses
list_to_port/1 of a number never issuedReturns a port termbadarg: only ports this program opened are admittedports
port_info/1,2 valuesid is a table index; memory, queue_size, locking describe the driverid is the port number; memory and queue_size are 0; locking is port_levelports
Writing a file opened only for reading{error, eacces} on Windows, {error, ebadf} elsewhere{error, ebadf} everywhereports
io:get_line, io:get_chars and file:read/2 in list mode on non-ASCII inputCharacters decoded per the device encodingOne list element per byteports
os:cmd/1 resultUnicode characters decoded from the outputThe output's bytesports
Empty arguments of {spawn_executable, F} on WindowsDropped from the command linePassed as ""ports
{exit_status, S} and eof of a spawned programUnspecified order, exit_status may arrive before the last dataAfter all data, before eofports
Busy portsA port with too much queued output suspends senders; port_command/3 force works on drivers that allow itOutput never suspends; force raises notsup on every driverports
{active, N} of socketsAn integer N counts messages before {tcp_passive, S}exit(badarg); only true, false and onceports
Socket tuning options (nodelay, keepalive, send_timeout, delay_send, buffers)Applied; other inet options (header, {packet, line}, http, ...) workThe listed tuning options are accepted and not applied; other options are exit(badarg)ports
Socket error reasonsEvery POSIX reason the system reportsThe common ones (econnrefused, eaddrinuse, econnreset, etimedout, ...); others are eioports
Port input while the connected process holds 1,024 messages and can runKeeps reading and deliveringStops delivering until the process's time slice ends; past 64 KiB held, stops reading, so the writing program blocksports
gen_tcp:close/1 with queued outputWaits up to the linger/send_timeout setting for the output to leaveReturns at once; the I/O thread sends the queued output, then closesports
monitor/2 typesprocess, port, time_offsetprocess and port; time_offset raises badarg (no time offset changes)processes
exit/2, exit_signal/2 to a referenceSends to the process alias, if the reference is an active oneNothing (no aliases)processes
Reference numbersMix a scheduler identifier and per-scheduler counters (#Ref<0.178111994.4235460610.214105>)One program-wide counter (#Ref<0.0.0.1>), so references order by creationterms

Errors, stack traces and reports

DifferenceOTPClauseOwner
Stack trace locations[{file, F}, {line, L}], error_info for error/3Always []; no error_infoABI
function_clause top frameArgument listArityABI
undef top frame{M, F, Args, []} of the missing functionThe calling function's framefuns
Failing BIF/operator framesPresent ({erlang, '+', Args, ...})Absent; nothing below the entry functionABI
Atom table full (list_to_atom/1)The emulator aborts (no more index entries in atom_tab) and writes a crash dumpclau: runtime failure: entry call failed: resource_limit, exit 70builtins
list_to_integer/1,2 with characters above 255Its first digits use only each character's low byte ([16#131] is 1)badargbuiltins
erlang:function_exported/3 of a BIFTrue for every BIF of the emulatorTrue only for the builtins this runtime providesbuiltins
Error reports of crashed processesSent to the logger, written later by its default handler (on standard output under erl, often lost when an escript halts first)Written on stderr when the process ends, before any later outputprocesses
Calls to functions that never returnCompiled as tail calls (caller missing from the trace)Ordinary calls (caller present)ABI
Host memory exhaustedThe emulator reports that it cannot allocate memory, writes a crash dump and stopsclau: runtime failure: entry call failed: out_of_memory, exit 70, no dumpruntime heap
Process memory capmax_heap_size (in words, heap and stack) kills the process with reason killed and logs an error report--max-heap / --max-stack (bytes) and the runtime-wide --max-memory fail the requesting process as resource_limit: clau: runtime failure: entry call failed: resource_limit, exit 70runtime heap
Program argumentsescript passes every argument to main/1; emulator flags (+t) come from %%! or ERL_FLAGSLeading runtime options (--max-atoms, --max-heap, --max-stack, --max-memory, --args-file, --) are taken out first; CLAUSE_FLAGS holds the same optionsexecutables
Uncaught exception in an ordinary entry moduleescript exits 127Exits 1 with one uncaught exception <class>: <reason> line (escript sources keep 127)executables
Compiler diagnosticserl_lint wording (variable 'X' is unbound) and warningsOwn wording (unbound variable X); OTP lint warnings are mostly not emitted (also unknown native record fields in access, update and patterns)semantic

Language edge cases

DifferenceOTPClauseOwner
Integer segment wider than the integer limit, value past it (<<V:4194241>> of all ones)The x86 JIT matches with an invalid term; using it crashes the VMNo matchterms
Zip group whose relaxed and strict generators share a variableSkip test keeps strict-pattern variables in the relaxed patternsA rejected step is skipped whenever the strict patterns match on their ownpatterns
Calling an external fun, M:F(Args) or apply/3 of a module outside the programLoads the module from the code path, undef when absentundef; library modules join the program only when it names them with a literal atomfuns, library
lists and mapsThe full modules; maps:keys/1, values/1, to_list/1, fold/3 follow the map's internal order (atom-table order for atom keys, hash order above 32 keys)The library subset; map functions follow key orderlibrary
Files without #! given as escriptsescript file.erl skips the first lineCompiled as ordinary modulesexecutables
Precompiled beam and archive escriptsRunNot supportedexecutables
A -spec whose types share no value with the inferred result, entry domain or a call's argumentsCompiles; Dialyzer may warnCompile error naming the declared and inferred typessemantic analysis

io

DifferenceOTPClauseOwner
Control sequences ~e ~f ~g ~x ~X ~+ ~# ~W ~P, modifier KFormattedbadargio
~p of containers nested more than 256 deepPrintedsystem_limitio
Field widths and ~ts precision with combining characters or \r\nCount grapheme clustersCount code pointsio
Negative precision or pad count (~.*c with -1)Loops foreverbadargio
io functions other than format/1,2, put_chars/1Existunknown module ioio