Standard library and built-ins
G# deliberately keeps its language-defined library small. Primitive types, collection intrinsics, channels, function values, and a few legacy built-in functions are provided by the compiler and evaluator. Most everyday library APIs are the .NET Base Class Library reached through imports and CLR interop; for example, printing in samples normally uses Console.WriteLine from the implicit or explicit System import. See CLR interop for constructors, members, delegates, events, generics, attributes, and other .NET surface.
Primitive types
The built-in primitive type symbols are exactly:
| Category | Types | Notes |
|---|---|---|
| Boolean | bool | Literals are true and false. |
| Unsigned integers | uint8, uint16, uint32, uint64, nuint | Width-bearing names are canonical. Older aliases such as uint and byte are not built-in primitive names. |
| Signed integers | int8, int16, int32, int64, nint | Unsuffixed integer literals default to int32. Older aliases such as int and long are not built-in primitive names. |
| Floating point and decimal | float32, float64, decimal | Unsuffixed float literals default to float64; suffixes include F, D, and M. |
| Text | char, string | char is one UTF-16 code unit; string is the CLR string type. |
| Top and no-value | object, void | object is the universal upper bound; void is the no-result type. |
| Absence | nil | nil is a special literal type that converts to nullable types, not a named runtime type. |
object accepts implicit boxing from G# values backed by CLR types and from user value types. Explicit unboxing is available for CLR value types. nil converts to T?, and postfix !! asserts a nullable value is present.
Operators on built-in types
G# does not perform cross-type operator promotion. Binary operators are defined for same-typed primitive operands unless otherwise noted.
bool:!,&,&&,|,||,^,==,!=.- Signed integers: unary
+, unary-,^; binary+,-,*,/,%,&,|,^,&^,<<,>>,==,!=,<,<=,>,>=. - Unsigned integers: unary
+,^; binary+,-,*,/,%,&,|,^,&^,<<,>>,==,!=,<,<=,>,>=. float32,float64,decimal: unary+, unary-; binary+,-,*,/,%,==,!=,<,<=,>,>=.char: unary+; binary==,!=,<,<=,>,>=.string:+,==,!=.object:==,!=.
Shift counts are int32. Compound assignments exist for the corresponding binary operators: +=, -=, *=, /=, %=, &=, |=, ^=, &^=, <<=, and >>=.
Intrinsic functions and operations
These names are recognized specially by the binder. They are language intrinsics, not methods imported from the BCL.
| Intrinsic | Form | Supported operands | Result | Import required |
|---|---|---|---|---|
len | len(x) | arrays, slices, strings, maps | int32 length/count | Gsharp.Extensions.Go |
cap | cap(x) | arrays, slices | int32 capacity | Gsharp.Extensions.Go |
append | append(slice, value) | first argument must be []T; second converts to T | new []T containing the appended value | Gsharp.Extensions.Go |
delete | delete(map, key) | first argument must be map[K,V]; key converts to K | no value; removes the key if present | Gsharp.Extensions.Go |
close | close(ch) | chan T | no value; completes the channel writer | Gsharp.Extensions.Go |
make | make(chan T) or make(chan T, capacity) | channel creation only | chan T | Gsharp.Extensions.Go |
| receive | <-ch | chan T | next T value, or the closed-channel default value | Gsharp.Extensions.Go |
| send | ch <- value | left side chan T; value converts to T | statement | Gsharp.Extensions.Go |
Every intrinsic above requires import Gsharp.Extensions.Go in the same compilation unit. The binder emits GS0317 for len, cap, append, and delete without the import — the message names the .NET-idiomatic alternative (.Length, .Count, .Remove(k), List[T].Add) when one exists. The channel-cluster intrinsics (close, make(chan T), <-, select) report GS0316 from the same root cause; a single import Gsharp.Extensions.Go unlocks both clusters.
make is currently special-cased only for channel creation; it is not a general allocator for slices or maps.
import Gsharp.Extensions.Go
let ch = make(chan int32, 3)
ch <- 1
close(ch)
let value = <-ch
Arrays and slices
Fixed arrays use [N]T; slices use []T. Literals use the same shape with an initializer body. Slices are backed by CLR arrays in the current implementation; append allocates and copies a new array.
import Gsharp.Extensions.Go
var nums = []int32{10, 20, 30}
Console.WriteLine(len(nums))
Console.WriteLine(cap(nums))
nums = append(nums, 40)
Console.WriteLine(nums[3])
Arrays and slices support indexing, index assignment when mutable, len, and cap. for i in 0 ... len(nums) is the common indexed loop form; for x in nums is the canonical iteration form. The legacy Go-style for x := range nums spelling is not supported. The len/cap/append calls above require import Gsharp.Extensions.Go; without the import, the equivalent .NET-idiomatic code uses nums.Length and a mutable List[int32] for Add semantics.
Maps
Map types are written map[K,V] and are backed by Dictionary<K,V>. Map literals use key/value entries, indexing reads values, index assignment writes values, delete removes a key, and len returns the current count. Both delete and len require import Gsharp.Extensions.Go; the .NET-idiomatic equivalents are counts.Remove("missing") and counts.Count.
import Gsharp.Extensions.Go
var counts = map[string,int32]{"gsharp": 1}
counts["gsharp"] = counts["gsharp"] + 1
delete(counts, "missing")
Console.WriteLine(len(counts))
The .NET Dictionary[K,V] type is also usable through CLR interop when you import System.Collections.Generic; that surface is the BCL, not the language-defined map intrinsic.
Sequences and iteration
sequence[T] is the G# type-clause spelling for IEnumerable[T]. Iterator functions that return a sequence can use yield expr. async sequence[T] is the spelling for IAsyncEnumerable[T], and await for iterates async streams. Sequence APIs beyond iteration come from the BCL, such as LINQ extension methods imported from System.Linq.
Gsharp.Extensions
The Gsharp.Extensions assembly ships with Gsharp.NET.Sdk and is referenced by every G# project automatically. It is the idiomatic helper layer over the BCL. Imports are always explicit — nothing under Gsharp.Extensions.* is auto-imported, even with the implicit-imports compiler option enabled. The assembly is organised by capability:
Gsharp.Extensions.Optional— extension methods onT?for projection, fallback, side-effects, and filtering.Gsharp.Extensions.Sequences— static builders and extension transformers oversequence[T].Gsharp.Extensions.Go— Go-flavored concurrency surface and built-ins gated behind the import.
Gsharp.Extensions.Optional
Extensions on T? for both reference-typed (T : class) and value-typed (T : struct) receivers. Each helper has two overloads with disjoint generic constraints; the G# binder picks the right one based on the receiver type.
| Symbol | Form | One-line description |
|---|---|---|
Map | func [T, U] (self T?) Map(f (T) -> U) U? | Apply f to the present value; pass null through unchanged. |
FlatMap | func [T, U] (self T?) FlatMap(f (T) -> U?) U? | Chain a projection that itself returns a U?, flattening the result. |
OrElse | func [T] (self T?) OrElse(default T) T | Return the present value or the eager fallback default. |
OrCompute | func [T] (self T?) OrCompute(default () -> T) T | Return the present value or invoke default() lazily for the fallback. |
OrThrow | func [T] (self T?) OrThrow(message string) T | Return the present value or throw InvalidOperationException(message). |
IfPresent | func [T] (self T?) IfPresent(action (T) -> void) | Invoke action only when the value is present; no-op otherwise. |
Filter | func [T] (self T?) Filter(pred (T) -> bool) T? | Keep the value when pred(value) is true; otherwise yield null. |
Each helper carries both a where T : class and a where T : struct overload, so the same names apply to reference- and value-typed T?. The G# binder picks the right overload based on the receiver's shape; this follows the constraint-aware overload-resolution rule.
Map, FlatMap, OrElse, OrCompute, IfPresent, and Filter (every overload) carry [MethodImpl(MethodImplOptions.AggressiveInlining)] so the JIT inlines them across the assembly boundary. OrThrow is intentionally not inlined so the failure site is preserved in stack traces.
Gsharp.Extensions.Sequences
Static builders on Sequences:
| Symbol | Form | One-line description |
|---|---|---|
Range | func Range(start int32, count int32) sequence[int32] | Lazy contiguous range [start, start + count). |
RangeStep | func RangeStep(start int32, end int32, step int32) sequence[int32] | Lazy strided range stopping before end; step must be non-zero (negative for descending ranges). |
Iterate | func Iterate[T](seed T, next (T) -> T) sequence[T] | Infinite sequence seed, next(seed), next(next(seed)), … — pair with Take(N) to bound. |
Repeat | func Repeat[T](value T) sequence[T] | Infinite sequence of value — pair with Take(N) to bound. |
Of | func Of[T](values ...T) sequence[T] | Wrap a params array as a sequence. |
Empty | func Empty[T]() sequence[T] | The empty sequence, allocation-free. |
Extension transformers on sequence[T]:
| Symbol | Form | One-line description |
|---|---|---|
Windowed | func [T] (self sequence[T]) Windowed(size int32) sequence[[]T] | Sliding windows of length size (stride 1). Empty when source is shorter than size. |
Chunked | func [T] (self sequence[T]) Chunked(size int32) sequence[[]T] | Non-overlapping chunks of size; the trailing chunk may be shorter. |
Indexed | func [T] (self sequence[T]) Indexed() sequence[(int32, T)] | Pair every element with its zero-based index. |
Pairwise | func [T] (self sequence[T]) Pairwise() sequence[(T, T)] | Yield adjacent pairs (s0, s1), (s1, s2), …. Empty when source has fewer than two elements. |
Interleave | func [T] (self sequence[T]) Interleave(other sequence[T]) sequence[T] | Round-robin the two sequences; trailing elements of the longer sequence flush at the end. |
Safe terminals:
| Symbol | Form | One-line description |
|---|---|---|
FirstOrNil | func [T] (self sequence[T]) FirstOrNil() T? (both T : class and T : struct) | First element or null if empty. Both reference- and value-typed overloads share the name; the binder picks the right one. |
LastOrNil | func [T] (self sequence[T]) LastOrNil() T? (both T : class and T : struct) | Last element or null if empty. |
SingleOrNil | func [T] (self sequence[T]) SingleOrNil() T? (both T : class and T : struct) | Single element, or null if empty or many. |
G#-shaped collectors:
| Symbol | Form | One-line description |
|---|---|---|
ToSlice | func [T] (self sequence[T]) ToSlice() []T | Materialise the sequence into a G# slice (T[] under the hood). |
ToMap (tuple form) | func [K, V] (self sequence[(K, V)]) ToMap() map[K,V] | Build a map from a sequence of key/value tuples. Throws on duplicate keys. |
ToMap (selector form) | func [T, K, V] (self sequence[T]) ToMap(keyFn (T) -> K, valueFn (T) -> V) map[K,V] | Project each element to a (K, V) pair, then build the map. |
FirstOrNil / LastOrNil / SingleOrNil (plus the *ValueOrNil companions), Indexed, Of, and Empty carry [MethodImpl(MethodImplOptions.AggressiveInlining)]. The iterator-block transformers (Windowed, Chunked, Pairwise, Interleave, Range, RangeStep, Iterate, Repeat) are intentionally not inlined — their bodies are compiler-generated state machines that the JIT does not inline.
Gsharp.Extensions.Go
The Go-flavored concurrency cluster — go, chan T, <-, select, close(ch), make(chan T) — and the Go-style built-ins len, cap, append, delete, make are all gated behind import Gsharp.Extensions.Go. The Intrinsic functions table above summarises the diagnostic codes the binder emits when the import is missing.
Functions, delegates, and closures
Function values use (P1, P2) -> R type clauses and function literals. Compatible function literals and method groups can convert to CLR delegate types during interop. Delegate construction and invocation are documented in CLR interop. The legacy func(P1, P2) R type-clause spelling continues to parse for one release with the GS0303 deprecation warning.
Console and legacy built-in functions
The curated documentation prefers .NET console APIs:
import System
Console.WriteLine("hello")
The compiler also contains legacy built-in functions print(text string), input() string, and rnd(max int32) int32. They are implemented by the evaluator as console/random helpers and are not the shape used by current reference samples.