Release Notes
1.29.2
Fixes
- Explicit type arguments on a generic function or method call are now
validated. A type argument that contradicts the actual arguments, such as
firstMatch<string>(listOfInts), is reported as an error rather than being silently discarded. The binding is also enforced at runtime, so a dynamically-typed argument that violates an explicit type argument throws, with the evaluator and bytecode VM in agreement.
1.29.1
Logging
- New
log.syslog(opts)destination sends structured log records to a syslog server or the local syslog daemon, framed as RFC 5424. It joins the existing logger family (stdout/stderr/file/toStream/custom), so the same level calls apply. Transport isudp(default),tcp, orlocal;udpandtcpwork on Linux, macOS/BSD, and Windows, whilelocal(the platform daemon socket) is Unix-only. The message body is the same JSON the other destinations emit, and facility, app name, and hostname are configurable. The logger connects when constructed (a bad address fails fast) and drops transient send failures rather than raising.
Fixes
sys.exit(code)now terminates the program cleanly with its code from any context, including inside an exportedmainand across module boundaries, on both runtimes. Previously it could surface as an uncaught error in those cases instead of exiting.
Documentation
- Logging now has its own reference chapter, split out of the observability chapter for discoverability.
1.29.0
Language
- Numeric literals now support scientific notation. Unsuffixed exponent
literals are exact
decimalvalues (1e3,1.5e-3,2E8); addffor IEEE 754 floats (1e308f). - Enums can now be backed by scalar
stringorintvalues:enum Status: string { Active = "active"; }. Backed variants expose a read-only.value,EnumName.from(value)returns the matching variant or throws, andEnumName.tryFrom(value)returns the variant ornull. Backing values must be unique literals, and variants cannot mix backing values with associated data.
Runtime
- Bytecode chunk format version bumped 75 to 76 so backed enum metadata round-trips through cached bytecode and built binaries.
- The experimental
geblang build --nativepath now rejects backed enums with a clear diagnostic instead of emitting incorrect Go; use the default bytecode build path for backed enums.
1.28.1
Language
- A type annotation on a
for-inloop variable is now checked and enforced, the same way a typed declaration is.for (int x in xs)is fine, but an unknown type name (for examplefor (var x in xs), wherevaris not a type) and an element-type mismatch (for (string x in [1, 2])) are reported bygeblang check,run, andtest. Omit the type to infer it from the iterable.
Tooling
geblang build --helpandgeblang test --helpnow list every flag, including--resourceand the--allow-ffi/--allow-onnx/--allow-process-control/--allow-browsercapability flags.- Editor autocomplete now shows the correct signatures for
io.captureStdoutandio.captureStderr: they take no arguments and return a capture stream you read withio.toString.
Documentation
- Corrected reference-manual examples that used syntax the language does not accept:
match block-body arms,
whenguards (the keyword isif), named enum fields, untyped lambda parameters, scientific-notation float literals, and a backtick string literal.
1.28.0
Standard library
- New
htmlmodule for parsing real-world HTML and querying it with CSS selectors.html.parse(source)returns the root node; nodes exposeselect/selectFirst(CSS3 selectors: type, class, id, attribute operators, descendant/child/sibling combinators, structural pseudo-classes, grouping),text,attr,attrs,tag,html(inner HTML),children, andparent. The parser is lenient, so malformed markup is repaired rather than rejected. See the HTML chapter in the standard-library reference.
Fixes
dir()now lists the methods of the native value types (n-dimensional arrays, data frames, series, distributions, complex numbers, generators, and html nodes) on the bytecode VM, matching the evaluator. Previously it returned an empty list for those types on the VM.
1.27.1
Standard library
- HTTP client responses now expose the final URL after redirects.
r.url()(and the index formr["url"]) returns the address the response was ultimately served from, so a request to a redirecting endpoint reports where it landed. It is an empty string for responses not produced by a request (for examplehttp.response(...)), and is carried throughwithStatus/withHeader/withBodyandtoDict.
1.27.0
Tooling
geblang run <script.gb> [args...]is now an explicit alias for the default script-running form (geblang <script.gb> [args...]). Both invoke the bytecode VM with evaluator fallback and accept the same flags (geblang run --vm-strict app.gb). The verb stays optional; the next token afterrunis always a script path, never re-read as another subcommand.- Cross-platform builds:
geblang build --runtime <path>embeds the bundle into a runtime compiled for another platform, and the newscripts/cross-build.shhelper drives it end to end (--target linux/amd64,darwin/arm64,windows/amd64, ...) using the Go toolchain, so any host can build for any target. - Geblang now builds and runs on Windows. FFI, local ONNX inference, and
advisory file locking report a clear unsupported error there; the
hnswvector-store backend uses an exact index; and interactive console widgets and the REPL line editor fall back to plain line input. See the bundling chapter.
Standard library
- The
mathmodule gains special functions and exact combinatorics. Special functions (real-valued):gamma,lgamma,beta,lbeta,erf,erfc,erfinv, and the Bessel functionsj0/j1/jn/y0/y1/yn. Combinatorics return exact integers at arbitrary precision:factorial,comb,perm,gcd,lcm, and the log binomial coefficientlcomb.factorial,perm, andcombrejectnabove 100000 to bound memory and CPU. - New
statsmodule: probability distributions as objects. A constructor (stats.normal(0, 1),stats.binomial(20, 0.5), ...) returns a distribution exposingpdf,cdf,ppf,mean,variance,std, andsample. Twelve distributions are included: normal, uniform, exponential, gamma, beta, chi-squared, Student's t, F, log-normal, Weibull, binomial, and Poisson.sample(n, {"seed": k})draws reproducibly into an ndarray. - The
statsmodule also gains hypothesis tests and confidence intervals, each returning a dict. Tests:tTestOneSample,tTestIndependent(pooled or Welch),tTestPaired,chiSquareTest,chiSquareIndependence,mannWhitneyU, andksTest. Intervals:confidenceIntervalMean,confidenceIntervalProportion, andconfidenceIntervalDiffMeans. - The
statsmodule also gains regression:linregress(simple linear least-squares, returning slope, intercept, correlation, r-squared, p-value, and slope standard error), pluspolyfit(least-squares polynomial fit) andpolyval(polynomial evaluation). - The
statsmodule also gains descriptive extensions:skewnessandkurtosis(population, with excess kurtosis),covariance(sample), andcorrcoef(Pearson correlation). - New
physicsmodule: twelve physical constants as zero-argument functions (c,G,planck,hbar,avogadro,boltzmann,gasConstant,elementaryCharge,electronMass,protonMass,stefanBoltzmann,gravity) andconvert(value, fromUnit, toUnit)covering length, mass, time (scale), and temperature (affine: C, F, K). - New
complexmodule: complex number value with two constructors (complex.of,complex.fromPolar), a full method set (re,im,abs,arg,conj,neg,exp,sqrt, and binaryadd/sub/mul/div/pow/equals), and operator overloads for+,-,*,/,**, unary-, and==. Plain numbers are promoted automatically on either side of a binary operation. - New
geomodule:haversineDistance,bearing,midpoint, anddestinationfor geodetic calculations on the sphere. Coordinates are in decimal degrees using a mean Earth radius of 6371 km. Distance functions accept an optional unit argument ("km"default,"m","mi","nmi");midpointanddestinationreturn{"lat", "lon"}dicts. - New
async.tasksmodule: high-level task combinators over the async core for callers who would rather hand off a function and data than manage tasks, channels, and semaphores directly.map/forEachrun a function over a collection concurrently (results in input order, optionalconcurrencycap, fail-fast);retryadds exponential-backoff retry;settleawaits every task without failing fast;anyreturns the first task to succeed; andparallelruns a list or dict of callables at once.
Fixes
- A closure created in one module and passed to
async.runfrom another module is now accepted on the bytecode VM, matching the evaluator. The VM previously rejected such a cross-module callable with "async.run expects a function".
Platform
- macOS and BSD support: the toolchain now builds and runs on macOS and the
BSDs. Terminal handling uses per-platform ioctl constants, and
sys.osVersionplusprocess.list/process.infoare implemented for those platforms.
1.26.0
Language
- Partial application: a call with one or more
_placeholder arguments now returns a new callable with those positions left open (add(_, 10),wrap(_, "-", _),open(mode: _)). Non-hole arguments are captured once at creation; the target resolves at application. Works across functions, methods, constructors, native builtins, module functions, and callable objects. (Partials over multiple same-arity overloads are resolved at application by the interpreter; compiled builds reject them statically - use a typed wrapper.)
1.25.0
Language
- Mixing
decimalandfloatin arithmetic - the deliberate precision wall that keeps adecimal's exactness from silently leaking into afloat- is now reported bygeblang checkwhen both operand types are statically known, not only at runtime. The error names both fixes and their tradeoff: cast withas float(fast, drops the decimal's exactness) oras decimal(keeps an exact type, adopts the float's binary imprecision). reflect.fields(class)now includes adockey on each field entry: the docblock written immediately before the field (a##line or a/** */block), ornullwhen there is none. This matches how class, function, and method docblocks were already surfaced.
Standard library
- New experimental
browsermodule: drive a headless Chrome/Chromium over the DevTools Protocol for functional testing and scripted control.browser.launchopens a browser; pages cangoto,waitFor,click,type/fill,evaluateJavaScript, readtext/content/title,screenshot/pdf, managecookies, list tabs, and intercept requests withroute(continue, block, or fulfill a mock response). It speaks the protocol directly with no external driver, is gated behind--allow-browser, and always terminates the browser on close. Needs Chrome installed (located via$GEBLANG_CHROMEor anexecutableoption).
Tooling
geblang fmtis substantially more robust. It now re-parses its own output and refuses to write anything that is not identical to the input at the AST level, so formatting can never silently change a program's meaning or break its syntax. By default it follows a width-aware layout standard (a 100-column target): explicit grouping parentheses, intentional blank lines, comments (including trailing same-line comments), and any construct the author split across lines (operator and method chains, list/dict/set literals, call argument lists) are kept, and a collection or argument list is wrapped onto one item per line when a single line would exceed the width.geblang fmtgained two formatting modes.--cleanproduces the minimal canonical form (drops redundant parentheses, flattens multi-line chains, concatenations, and collections onto one line);--strip-commentsremoves all comments. The flags are independent and may be combined.
1.24.1
Bundling
- Built binaries (
geblang build) can now carry privileged capabilities baked in at build time, so they run with no launch flags. Declare them ingeblang.yamlunderpermissions:-ffi(existing),onnx: true,processControl: true- and/or passgeblang build --allow-ffi <path-or-glob>/--allow-onnx/--allow-process-control; build flags add to what the manifest declares. - The
onnxandprocessControlpermissions also enable those capabilities forgeblang run/geblang testin the project, matching howpermissions.ffialready behaved. A binary (or run) without them stays locked down: a gated call throwsPermissionError.
Tooling
- The
geblangbinary now embeds the source standard library, so a standalone binary - copied anywhere, with noGEBLANG_STDLIBand no stdlib directory beside it - resolves source stdlib modules (llm,rag,vectorstore, ...) with no setup. An on-disk stdlib (a repo checkout orGEBLANG_STDLIB) still takes precedence, so developing against a working copy is unchanged.
Runtime
- A running program now starts a background memory sweeper that periodically
returns freed heap pages to the OS, so a long-running server's RSS no longer
stays pinned at its allocation high-water mark after a burst (e.g. buffering
large uploads). It is on by default and tuned with
GEBLANG_GC(offto disable),GEBLANG_GC_INTERVAL(default30s), andGEBLANG_GC_THRESHOLD_MB(default64).profile.gc()still forces a collection on demand. - Streaming HTTP response handles (
http.requestStream,http.fetchStream) are now released as soon as the stream is fully read or closed, and any left open are swept at shutdown, so a long-running server that streams many responses no longer accumulates handles for the life of the program.
1.24.0
Standard library
vecmathgainednormalize(L2-normalize a vector, or each vector in a list) andsemanticSearch(queries, corpus, k, metric), the multi-query form oftopKthat returns the top-k corpus matches per query as{index, score}.- The
llmclient gainedmodels()(list the models available to the account) andembedBatch(texts, opts)(embed many strings in one call, returning{vectors, model, usage}), across the OpenAI, Anthropic, and Bedrock providers (Anthropic has no embeddings API, soembedBatchthrows there). - The
llmchatmethod now supports tool / function calling. Passopts.tools(provider-neutral{name, description, parameters}schemas); when the model calls a tool the result carriestoolCalls({id, name, arguments}), and you continue the conversation with a{role: "tool", toolCallId, content}message. One portable shape across OpenAI, Anthropic, and Bedrock Claude models. - New
http.requestStream(options)performs a request and returns aStreamResponsewhoseread()yields the body line-by-line as it arrives (nullat end), the client-side analog of server-sent events.status(),headers(),done(), andclose()round it out. - The
llmclient gainedchatStream(messages, opts, callback): streaming chat that invokescallbackwith each content delta and returns the assembled result. Supported for OpenAI and Anthropic (server-sent events); Bedrock uses a binary event-stream protocol and throws there. - New experimental local-model modules.
transformers.tokenizeruns WordPiece tokenization from a HuggingFacetokenizer.json(BERT-family encoders), returning paddedinput_ids/attention_mask/token_type_ids.onnx.session(modelPath)loads an ONNX model for cgo-free local inference via ONNX Runtime;Session.runmaps int64 input ndarrays to float64 output ndarrays. Both are gated behind the new--allow-onnxlaunch flag, and ONNX Runtime is located viaopts.libPath/$GEBLANG_ONNXRUNTIME.transformers.poolreduces a[batch, seq, dim]hidden state + mask to[batch, dim]sentence embeddings (mean / cls / max, L2-normalized), so tokenize ->onnx.session-> pool gives fully local, offline sentence embeddings that feed straight intovecmath/vectorstore/rag.rag.LocalEmbedder(modelDir)wraps that pipeline as a drop-inEmbedder, for a fully on-device index/retrieve loop with no API calls. - HTTP client failures now carry a precise, catchable class: a timeout raises
TimeoutErrorand a TLS/certificate failure raisesTlsError, both newIOErrorsubclasses (socatch (IOError e)still catches them) - so callers can retry on a timeout but fail fast on a bad certificate. The same classification applies engine-wide to any timeout/TLS failure. A mid-stream read failure onhttp.requestStreamnow surfaces as a catchable error rather than looking like a clean end of stream.
1.23.3
Standard library
- New
search/searchPatternmethods on lists, dicts, and strings return every matching locator (not just the first): list indices, dict keys (matched on their values), or string positions.search(value)matches by equality,search(callable)by a predicate, andsearchPattern(regex)by regular expression. Each returns an empty list when nothing matches.
1.23.2
Standard library
- Lists gained
fill(value, count): it appendscountcopies ofvaluein place and returns the list, so it chains.countmust be>= 0; a typed list rejects a value of the wrong element type.
1.23.1
Language
- A cast to a primitive followed by
<now parses as a comparison:x as int < yis(x as int) < y. Primitive types never take type arguments, so<after one is the less-than operator. Generic casts such asx as list<int>are unchanged.
Standard library
cryptgained real X.509 chain support:crypt.verifyCertChainverifies a certificate chain's signatures up to a trusted root (throwing on failure),crypt.parseCertnow also returns thepublicKeyPEM and rawextensions,crypt.asn1Decodedecodes DER into a nested structure, andcrypt.parseAndroidAttestationreads the Android Key Attestation extension.- SQLite connections accept tuning options on the
db.Connection({...})options-dict, applied as per-connection pragmas at connect time:wal,synchronous,foreignKeys,busyTimeoutMs,cacheSizeKb,mmapSizeMb, andtempStoreMemory. Each is explicit (wal: truesets only WAL). A newconnection.optimize()runsPRAGMA optimizefor query-planner maintenance.
Type checking
- Division
/is true division and its result type isdecimal(orfloat), even when the operands divide evenly. Assigning a division result to anint(int n = a / b) is now a compile-time error on every path (check/test/run/build), reported ascannot assign decimal to int. Use//for an integer (floor) result, or(a / b) as intto truncate. This closes a case where the bytecode VM crashed at runtime while the evaluator produced a decimal.
Modules and packages
- Path dependencies in
geblang.yamlnow accept absolute paths, a leading~for the home directory, and$VAR/${VAR}environment references. Relative paths are still resolved against the manifest. - A scheme-less
gitdependency value (for examplegithub.com/acme/httplib) is now resolved ashttps://.... Explicit schemes and the scp-likegit@host:pathform are left untouched.
1.23.0
Standard library
iofile handles gained random access:io.seek(handle, offset, whence)(whence"start"/"current"/"end"),io.tell,io.truncate, andio.atEnd. Open modes now includer+,w+,a+, and the exclusive-createx/x+.- New filesystem helpers:
io.copy,io.copyTree,io.move(rename with a cross-device copy fallback),io.lstat,io.scanDir,io.touch, andio.writeTextAtomic. Theio.stat/io.lstatdictionaries gainedisFileandisSymlink. - New
filemodule: aFileobject (file.open(path, mode)) wraps a handle with method-style read / write / seek,with-block auto-close, and line iteration.streams.IOStreamgainedseek/tell/truncate/atEnd.
Fixes
reflect.classes()called from inside an imported module now returns the entry module's classes on the bytecode VM, matching the evaluator (the VM previously omitted them), so whole-program class scans behave identically on both backends.- A class declared in the entry file now resolves the entry file's top-level
bindings (imports and module-level
let/const) when its methods are dispatched from another module on the bytecode VM, matching the evaluator.
Native compilation (experimental)
- Module-level
let/constread by a function now compiles natively, in both imported modules and the entry module: the binding lowers to a package-level value so the function can see it. A module's functions can also call each other. These previously reported as unsupported or failed to build with an undefined-symbol error. list.join(sep)compiles natively for lists of any element type.- Comparing a list, dict, set, or bytes value to
nullcompiles natively (a nil check), matching the interpreter; structural==between two collections remains deferred. - Unwrapping a nullable dict with
as(e.g.value as dict<string, any>after a null check) compiles natively. io.mkdircompiles natively.
1.22.0
Language
-
Enums expose two operations on the enum type itself.
EnumName.values()returns a list of the simple (nullary) variants in declaration order, andEnumName.fromName(s)resolves a variant by its exact, case-sensitive name, returning the variant ornullwhen none matches. Tagged variants are excluded from both, since a bare name cannot construct a variant that carries fields. Identical on both backends. -
Numeric-check predicates let a value be tested before converting, instead of catching a failed cast. On strings:
isInt(),isDecimal(), andisNumeric()report whether the string parses as an integer, a decimal, or either, using the same rules astoInt()/toDecimal()(sos.isInt()is true exactly whens.toInt()would succeed, bases and underscore separators included). On numbers:float.isInt()anddecimal.isInt()report whether the value is a whole number (NaNand infinity are not). Identical on both backends. -
zrange(start, end[, step])is the exclusive, Python-style counterpart to the inclusiverangebuiltin: it omits the end value (zrange(0, 5)is[0, 1, 2, 3, 4]) and adds a one-arg formzrange(n)that ranges from 0. It returns an eagerlist<int>;collections.rangeremains the lazy exclusive form. Identical on both backends.
Tooling
geblang checknow verifies that amatchover an enum subject handles every variant. A match that omits a variant and has nodefaultis reported aswarning[match-nonexhaustive], naming the missing variants. It is advisory (the code still runs and throwsMatchErroronly if an unhandled value reaches the match); a variant handled only by a guarded case counts as missing, since the guard may be false. Surfaced in the editor through the language server.
Fixes
optionandresult: the absent/error path no longer binds the generic type to null.none(),err(), andofNullable(null)now keep their declared type, so a typedunwrapOrfallback works (divide(5, 0).unwrapOr(-1)returns-1instead of throwing),orNull()returns?T, and the documented examples run.- A private in-memory SQLite database (
:memory:) now pins its connection pool to a single connection. Each:memory:connection is a separate database, so concurrent access previously could open a second, empty one (no such table); pinning makes concurrent reads and writes share one database. Usefile::memory:?cache=sharedor a file path for a larger pool. reflect.fields,reflect.methods,reflect.classNameand the other reflect introspection calls now accept a nestedreflect.class(value)(orreflect.function) argument identically on both backends. Previously the bytecode backend rejected the nested form with a non-literal argument, requiring the class to be bound to a variable first.cli.choose(label, options, default)accepts a literal default index again; a small integer literal was wrongly rejected as "default index must be int".sys.run(command, args)accepts its documentedlist<string>argument form again (it was rejected with "arguments must be strings"); the trailing-varargs form keeps working.
Standard library
dataframe.filterFn(row -> bool)filters a frame with a per-row Geblang predicate, each row passed as a dict of column name to value, complementing the faster columnwise expression filter. Athrowinside the predicate propagates and is catchable, and predicates run safely from concurrent async tasks. Identical on both backends.json.parseAs(andyaml/toml/xmlparseAs) now reconstructs nested class fields recursively: a field whose declared type is another class, or alist/dictof one, is itself deserialized into an instance, so a whole object tree comes back fully typed (including across modules).anyand primitive fields keep their raw parsed value, a class with__deserializestill controls its own nesting, and a value whose shape does not match the declared field is left as-is. Identical on both backends.cli.multiChoose(label, options)selects several options at once: an arrow-key checkbox UI on a terminal (up/down or j/k move, space toggles,atoggles all, enter confirms, q/ctrl-c cancels) with a numbered comma-separated fallback when stdin is not a terminal. An optional third argument pre-checks options by index.io.withStdin(input, body)runsbodywith the console readers consuminginput(line input or raw key sequences) instead of stdin, then restores it. The interactive UI is suppressed while it runs, so it makes prompts (includingcli.multiChoose) testable. Returns the body's value.
Native compilation (experimental)
geblang build --nativenow lowers directascasts from a string tointorfloat, and from a non-bool tobool, matching the interpreter (these previously reported as unsupported). The new numeric-check predicates compile natively as well.- The enum static surface (
values()/fromName()) compiles natively, as do nullable enums (?Color) and equality on nullable value-types (?int == 5), which previously reported as unsupported or failed to build. as decimalcasts (from an int, float, string, or dynamic value) compile natively, matching the interpreter.
1.21.0
Language
- Enums can declare instance methods, callable on any variant with
thisbound to the receiving variant and read viamatch this. There is one method body per name; per-variant behaviour is expressed inside it, not as an override table. Methods sit beside the existing associated-value and backed-scalar access (which still resolve first), and a method named like a built-in variant accessor is a compile error. Enums stay immutable. - Enums can implement interfaces with
implements. Conformance is checked (a missing method is an error agreeing across check, test, run, and build), interface defaults apply where an enum leaves a method unimplemented, and an enum value flows into an interface-typed slot with dispatch landing on the enum's method. The bareenum Name { A, B }form is unchanged. Identical on both backends.
Native compilation (experimental)
geblang build --nativeis a preview that compiles a program to a standalone native binary by transpiling it to Go, for a speedup on compute-heavy code. It supports a growing subset of the language and standard library and fails the build with a clear diagnostic on anything unsupported, never producing a binary that behaves differently from the interpreter; use plaingeblang buildfor the full language. The supported set is unstable and will change between releases. See the bundling chapter for what is and is not supported.
Process and system management
sys.osVersion()returns the OS kernel and release string, complementingsys.platform()andsys.arch().- The
processmodule reports the running process's own identity and credentials:pid(),ppid(),uid(),gid(),euid(),egid(), andgroups(). - It can inspect other running processes with
process.list(),process.info(pid), andprocess.exists(pid). - Privileged control of arbitrary processes (
process.setuid(),process.setgid(),process.kill(pid),process.signal(pid, sig)) is gated behind the opt-in--allow-process-controllaunch flag and raises aPermissionErrorwhen the flag is absent. Handle methods acting on a process the script launched itself stay ungated. - Identity and credential functions are unix-oriented and process enumeration is Linux-first; a function unsupported on the host platform raises a clear, catchable error rather than returning an empty or wrong result.
Fixes
- Nullable typed-collection element tags accept
null: anullmay be written into alist<?int>or adict<string, ?int>on every write surface (push,insert, index assignment,set,add), while a non-nullablelist<int>still rejects it. Identical on both backends. - The bytecode VM's unary-minus type error now matches the evaluator's wording.
x as anyis accepted as a widening no-op on the bytecode VM, matching the evaluator; it previously raised a spurious "cannot cast" error.geblang buildreports a missing or incompatible entrymainat build time and writes no binary, instead of failing only when the built binary runs. The entry module mustexport func main()orexport func main(list<string> args), optionally returning: int.
1.20.0
Language
-
Explicit constructor type arguments are enforced at runtime on both runtimes:
Box<string>(42)throwsRuntimeError(previously the bytecode runtime accepted it silently while the evaluator threw, and the recorded bindings could contradict the stored value). Subtype arguments still pass and calls without explicit type arguments stay inference-open. Enforcement applies same-module and across module boundaries. This is a behavior change for code that relied on the silent acceptance. -
Method parameters typed with a class-level type parameter are enforced against the instance's reified bindings on both runtimes:
put(42)on aBox<string>throwsBox.put expects T for parameter 'value', got int. Inherited methods enforce extends-clause bindings (IntBox extends Box<int>); a method's own type parameters still bind per call. This is a behavior change for code that relied on uncheckedTparameters. -
instanceofanswers parameterized checks against user generic classes from the instance's reified bindings, with the same invariant model aslist<int>:b instanceof Box<string>is true when the class matches andTis bound tostring(previously always false). Bare-name checks are unchanged; type-parameter names in the argument list resolve in generic frames. -
A declaration annotation over a direct constructor call of the same class validates like explicit type arguments:
Box<int> x = Box("text")is rejected (statically when the contradiction is visible, at runtime otherwise). The annotation still wins over inference for the recorded bindings;letwithout an annotation stays inference-open. This is a behavior change. -
Explicit type arguments resolve constructor overloads: with
Box(T value)andBox(int value)declared,Box<string>(42)selectsBox(int value)instead of reporting an ambiguous overload. Bindings only break ties; single-candidate mismatches keep the precise per-parameter error. -
Calling a function declared to return
anyin a statically typed context (typed parameter, generic constructor argument, typed declaration) no longer fails compilation on the bytecode runtime; the value is validated at runtime on both runtimes, matching the evaluator's long-standing behavior. -
The element-tag write barrier on typed collections is now hierarchy-aware and complete. Subclass and implementer writes pass (
list<Animal>.push(Dog())previously threw - name-equality only), and the barrier covers every mutation surface: index assignment (xs[0] = v),list.set, dict key AND value writes (d[k] = v,dict.set), andset.addwere previously unchecked. Covariant generic passing is now sound: alist<Dog>received aslist<Animal>rejects aCatwrite against its real tag. This is a behavior change in both directions. -
Generic constraints accept primitive and class leaves alongside interfaces, combined with
|and&:<T implements string|int>now works (previously primitives never satisfied a constraint), and a class leaf is satisfied by the class or any subclass. A bare form drops the keyword:<T string|int>on functions and classes means the same thing. Constraint-violation messages are now identical on both runtimes (type bool does not satisfy constraint string|int for type parameter T).
Standard library
ndarrayanddataframegain operator support:+ - * / **and unary-work elementwise on arrays and numeric series (scalars on either side, broadcasting included),< > <= >=on arrays return 0/1 masks (a.where(a > 2.0)), and dataframe expressions build with operators (df.col("age") > 30,df.col("price") * df.col("qty")).==and!=keep their language-wide meaning everywhere;eq()/ne()cover elementwise equality. Series operator results are ndarrays.dataframe.pivot({"index", "columns", "values", "agg"})spreads a column's values into per-value columns with aggregation (defaultsum; any groupBy aggregator exceptcollect).
Static analysis
geblang check(and the compile path ofrun,test, andbuild) reportserror[semantic]when a constructor call's explicit type arguments contradict a statically-known argument type (Box<string>(42)). Covariant passing stays clean;any-typed, named, and spread arguments defer to the runtime check.- An unknown type name in an
instanceoftarget is now an error instead of silently evaluating to false (x instanceof Nope). Cross-module trailing-name matching and generic type parameters stay recognized; the check stands down when an import's surface cannot be resolved. - An unknown type name in a generic constraint clause
(
<T implements Nope>) is flagged at the declaration, like every other annotation position, instead of only failing when the function is called.
Older releases (1.0.0 through 1.19.0) are archived: see the release notes archive.