Skip to content

Instantly share code, notes, and snippets.

@johannes-riecken
Created March 16, 2025 09:33
Show Gist options
  • Save johannes-riecken/8efdebb75e78c6045ae499115aea142a to your computer and use it in GitHub Desktop.
Save johannes-riecken/8efdebb75e78c6045ae499115aea142a to your computer and use it in GitHub Desktop.
Code examples from "Definition-Checked Generics" talk at CppNow 2023
interface RNGInterface {
let Result: type;
fn Random[addr self: Self*]() -> Result;
}
class BaseRNGClass { ... }
class FancyRNG {
extend base: BaseRNGClass;
extend impl as RNGInterface where .Result = f64 {
fn Random[addr self: Self*]() -> f64 { ... }
}
}
fn GenericFunction[T:! RNGInterface](r: T*) -> T.Result {
return r->Random();
}
interface RNGInterface {
let Result: type;
fn Random[addr self: Self*]() -> Result;
}
class BaseRNGClass { ... }
class FancyRNG {
extend base: BaseRNGClass;
extend impl as RNGInterface where .Result = f64 {
fn Random[addr self: Self*]() -> f64 { ... }
}
}
fn GenericFunction[T:! RNGInterface](r: T*) -> T.Result {
return r->Random();
}
interface RNGInterface {
let Result: type;
fn Random[addr self: Self*]() -> Result;
}
class BaseRNGClass { ... }
class FancyRNG {
extend base: BaseRNGClass;
extend impl as RNGInterface where .Result = f64 {
fn Random[addr self: Self*]() -> f64 { ... }
}
}
fn GenericFunction[T:! RNGInterface](r: T*) -> T.Result {
return r->Random();
}
interface RNGInterface {
let Result: type;
fn Random[addr self: Self*]() -> Result;
}
class BaseRNGClass { ... }
class FancyRNG {
extend base: BaseRNGClass;
extend impl as RNGInterface where .Result = f64 {
fn Random[addr self: Self*]() -> f64 { ... }
}
}
fn GenericFunction[T:! RNGInterface](r: T*) -> T.Result {
return r->Random();
}
interface MulWith(U:! type) {
let Result:! type = Self;
fn Op[self: Self](rhs: U) -> Result;
}
class Point {
var x: f64;
var y: f64;
impl as MulWith(f64) where .Result = Point {
fn Op[self: Self](scale: f64) -> Point {
return {.x = self.x * scale, .y = self.y * scale};
}
}
}
fn Double(p: Point) -> auto {
let scale: f64 = 2.0;
return p * scale;
// => p.(MulWith(typeof(scale)).Op)(scale)
// => p.(MulWith(f64).Op)(scale)
}
interface MulWith(U:! type) {
let Result:! type = Self;
fn Op[self: Self](rhs: U) -> Result;
}
class Point {
var x: f64;
var y: f64;
impl as MulWith(f64) where .Result = Point {
fn Op[self: Self](scale: f64) -> Point {
return {.x = self.x * scale, .y = self.y * scale};
}
}
}
fn Double(p: Point) -> auto {
let scale: f64 = 2.0;
return p * scale;
// => p.(MulWith(typeof(scale)).Op)(scale)
// => p.(MulWith(f64).Op)(scale)
}
interface MulWith(U:! type) {
let Result:! type = Self;
fn Op[self: Self](rhs: U) -> Result;
}
class Point {
var x: f64;
var y: f64;
impl as MulWith(f64) where .Result = Point {
fn Op[self: Self](scale: f64) -> Point {
return {.x = self.x * scale, .y = self.y * scale};
}
}
}
fn Double(p: Point) -> auto {
let scale: f64 = 2.0;
return p * scale;
// => p.(MulWith(typeof(scale)).Op)(scale)
// => p.(MulWith(f64).Op)(scale)
}
interface MulWith(U:! type) {
let Result:! type = Self;
fn Op[self: Self](rhs: U) -> Result;
}
class Point {
var x: f64;
var y: f64;
impl as MulWith(f64) where .Result = Point {
fn Op[self: Self](scale: f64) -> Point {
return {.x = self.x * scale, .y = self.y * scale};
}
}
}
fn Double(p: Point) -> auto {
let scale: f64 = 2.0;
return p * scale;
// => p.(MulWith(typeof(scale)).Op)(scale)
// => p.(MulWith(f64).Op)(scale)
}
interface MulWith(U:! type) {
let Result:! type = Self;
fn Op[self: Self](rhs: U) -> Result;
}
class Point {
var x: f64;
var y: f64;
impl as MulWith(f64) where .Result = Point {
fn Op[self: Self](scale: f64) -> Point {
return {.x = self.x * scale, .y = self.y * scale};
}
}
}
fn Double(p: Point) -> auto {
let scale: f64 = 2.0;
return p * scale;
// => p.(MulWith(typeof(scale)).Op)(scale)
// => p.(MulWith(f64).Op)(scale)
}
choice Ordering {
Less,
Equivalent,
Greater,
Incomparable
}
interface OrderedWith(U:! type) {
fn Compare[self: Self](u: U) -> Ordering;
}
fn StringLess(s1: String, s2: String) -> bool {
return s1 < s2;
// => s1.(OrderedWith(String).Compare)(s2) == Less
}
fn StringGreater(s1: String, s2: String) -> bool {
return s1 > s2;
// => s1.(OrderedWith(String).Compare)(s2) == Greater
}
choice Ordering {
Less,
Equivalent,
Greater,
Incomparable
}
interface OrderedWith(U:! type) {
fn Compare[self: Self](u: U) -> Ordering;
}
fn StringLess(s1: String, s2: String) -> bool {
return s1 < s2;
// => s1.(OrderedWith(String).Compare)(s2) == Less
}
fn StringGreater(s1: String, s2: String) -> bool {
return s1 > s2;
// => s1.(OrderedWith(String).Compare)(s2) == Greater
}
interface OrderedWith(U:! type) {
fn Compare[self: Self](u: U) -> Ordering;
default fn Less[self: Self](u: U) -> bool {
return self.Compare(u) == Ordering.Less;
}
default fn LessOrEquivalent[self: Self](u: U) -> bool {
let c: Ordering = self.Compare(u);
return c == Ordering.Less or c == Ordering.Equivalent;
}
default fn Greater[self: Self](u: U) -> bool {
return self.Compare(u) == Ordering.Greater;
}
default fn GreaterOrEquivalent[self: Self](u: U) -> bool {
let c: Ordering = self.Compare(u);
return c == Ordering.Greater or c == Ordering.Equivalent;
}
}
interface OrderedWith(U:! type) {
fn Compare[self: Self](u: U) -> Ordering;
default fn Less[self: Self](u: U) -> bool {
return self.Compare(u) == Ordering.Less;
}
default fn LessOrEquivalent[self: Self](u: U) -> bool {
let c: Ordering = self.Compare(u);
return c == Ordering.Less or c == Ordering.Equivalent;
}
default fn Greater[self: Self](u: U) -> bool {
return self.Compare(u) == Ordering.Greater;
}
default fn GreaterOrEquivalent[self: Self](u: U) -> bool {
let c: Ordering = self.Compare(u);
return c == Ordering.Greater or c == Ordering.Equivalent;
}
}
interface Printable {
fn Print[self: Self]();
}
class Vector(template T:! type) { ... }
impl forall [T:! Printable] Vector(T) as Printable {
fn Print[self: Self]() {
var first: bool = true;
for (elem: T in self) {
if (not first) { ", ".Print(); }
elem.Print();
first = false;
}
}
}
interface As(Dest:! type) {
fn Convert[self: Self]() -> Dest;
}
impl String as As(Path) {
fn Convert[self: String]() -> Path {
return Path.FromString(self);
}
}
let config_file: Path = "/etc/myutil.cfg" as Path;
// => ("/etc/myutil.cfg").(As(Path).Convert)()
interface ImplicitAs(Dest:! type) {
extends As(Dest);
// Inherited from As(Dest):
// fn Convert[self: Self]() -> Dest;
}
impl String as ImplicitAs(StringView) {
fn Convert[self: String]() -> StringView {
return StringView::Make(self.Data(), self.Data() + self.Size());
}
}
fn Greet(s: StringView) { Print("Hello, {0}", s); }
fn Main() -> i32 {
Greet("audience");
// => Greet(("audience").(ImplicitAs(StringView).Convert)()
return 0;
}
impl forall [U:! type, T:! As(U)]
Optional(T) as As(Optional(U));
impl forall [U:! type, T:! ImplicitAs(U)]
Optional(T) as ImplicitAs(Optional(U));
impl forall [T:! type]
NullOpt as ImplicitAs(Optional(T));
interface CommonTypeWith(U:! type) {
let Result:! type
where Self impls ImplicitAs(.Self) and
U impls ImplicitAs(.Self);
}
class InternedString { ... }
impl InternedString as CommonTypeWith(String)
where .Result = StringView {}
fn SelectString(condition: bool, s: String, i: InternedString) -> StringView {
// Carbon version of ... ? ... : ... in C++:
return if condition then s else i;
}
fn SelectLongString(s: String, i: InternedString, v: StringView) -> auto {
if (s.Size() > 20) {
return s;
} else if (i.Size() > 20) {
return i;
} else {
return v;
}
}
interface MulWith(U:! type) {
let Result:! type = Self;
fn Op[self: Self](rhs: U) -> Result;
}
class Point {
var x: f64;
var y: f64;
impl as MulWith(f64) where .Result = Point {
fn Op[self: Self](scale: f64) -> Point;
}
}
fn Double(p: Point) -> auto {
let scale: f64 = 2.0;
return p * scale;
// => p.(MulWith(f64).Op)(scale)
}
fn GenericDouble[T:! MulWith(f64)](x: T) -> auto {
let scale: f64 = 2.0;
return x * scale;
// => p.(MulWith(f64).Op)(scale)
}
interface MulWith(U:! type) {
let Result:! type = Self;
fn Op[self: Self](rhs: U) -> Result;
}
class Point {
var x: f64;
var y: f64;
impl as MulWith(f64) where .Result = Point {
fn Op[self: Self](scale: f64) -> Point;
}
}
fn Double(p: Point) -> auto {
let scale: f64 = 2.0;
return p * scale;
// => p.(MulWith(f64).Op)(scale)
}
fn GenericDouble[T:! MulWith(f64)](x: T) -> auto {
let scale: f64 = 2.0;
return x * scale;
// => p.(MulWith(f64).Op)(scale)
}
interface MulWith(U:! type) {
let Result:! type = Self;
fn Op[self: Self](rhs: U) -> Result;
}
class Point {
var x: f64;
var y: f64;
impl as MulWith(f64) where .Result = Point {
fn Op[self: Self](scale: f64) -> Point;
}
}
fn Double(p: Point) -> auto {
let scale: f64 = 2.0;
return p * scale;
// => p.(MulWith(f64).Op)(scale)
}
fn GenericDouble[T:! MulWith(f64)](x: T) -> auto {
let scale: f64 = 2.0;
return x * scale;
// => p.(MulWith(f64).Op)(scale)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment