Skip to main content
Version: Next

Types and values

G# combines Go-like aggregate and collection syntax with CLR type identity. Use the language specification for exact syntax.

Nil and nullable values

nil is the null literal. Nullable types are written with ?, such as string?. A non-null value converts to its nullable form, and nil converts to nullable types. null is not a literal in G#. Use ?: for null coalescing, ?. for null-conditional access, and !! only when a failed assertion should throw immediately.

This model follows ADR-0001.

Primitive values

The built-in primitive names are explicit and CLR-backed: bool, signed and unsigned width-bearing integers, native integers, float32, float64, decimal, char, string, object, and void. object is the universal upper bound, so values can widen or box to it. See ADR-0044, ADR-0045, ADR-0046, and ADR-0049.

Numeric operators are defined for matching primitive types; G# does not silently promote arbitrary mixed numeric operands. Use explicit conversions when crossing type families.

Arrays, slices, and maps

Arrays have fixed length and are written [N]T. Slices are written []T and are backed by CLR arrays in the current implementation. append returns a new array-backed value after copying. Maps are written map[K]V and are backed by Dictionary<K,V>.

let numbers = []int32{1, 2, 3}
let fixed = [3]int32{1, 2, 3}
let names = map[int32]string{1: "one", 2: "two"}

Slice design rationale is in ADR-0016.

Structs, data structs, records, and inline structs

Use plain struct for value-like aggregates, data struct for structural equality and copy/update ergonomics, record as the data-struct alias, and inline struct for a single-field value wrapper.

samples/DataStruct.gs
package GSharp.Example.DataStruct

import System

type Point data struct {
X int32
Y int32
}

var p = Point{X: 3, Y: 4}
var q = Point{X: 3, Y: 4}
Console.WriteLine(p == q)

Rationale: ADR-0029, ADR-0032, and ADR-0033.

Classes and interfaces

Classes are reference-like and support primary constructors, explicit init constructors, fields, methods, properties, events, inheritance, and shared members. Classes are sealed by default for inheritance unless marked open. Methods that can be overridden are marked open; overriding methods use override. See ADR-0003 and ADR-0017.

Interfaces define method, property, and event signatures. Current implementation treats interface bodies as signatures only; method bodies are diagnosed even though ADR-0018 records future-facing rationale for defaults.

Enums

Enums are closed sets of named values. They cannot be generic and must contain at least one member. Equality and switch exhaustiveness diagnostics understand enum members.

type Status enum { Pending, Complete, Failed }

Sequences and channels

sequence[T] maps to IEnumerable<T> and is produced with iterator functions that use yield. async sequence[T] maps to asynchronous enumeration and is consumed with await for. chan T represents channels created with make(chan T) or make(chan T, capacity).

Function types and delegates

Function types use func(...) R. Async function type clauses use async func(...) R and represent task-returning functions. Function values can convert to compatible CLR delegate types, including named delegates and common Action or Func shapes.

A named delegate type is declared with type Name = delegate func(...) (ADR-0059) and emits as a real CLR MulticastDelegate-derived type. Use a named delegate when you want a stable, C#-visible handler type (for example, as the type of a G# event):

type Handler = delegate func(sender Object, e EventArgs)

type Button class {
event Click Handler
}

Generics and variance

G# uses bracketed generics: declarations such as type Box[T any] class { ... } and instantiations such as Box[int32]. Type parameters can use in and out variance markers and named constraints. The implementation supports metadata specs and inference, but some open or partially constructed generic shapes are erased to object in emit paths. See ADR-0004, ADR-0020, and ADR-0021.