On Tuesday, 24 February 2026 at 16:56:57 UTC, monkyyy wrote:
Its c-like code and enabling "unsafe" void* hacks.

Gist code from last night(I already see one improvement and expect more):

```d
#!opend test app
import std;
import core.stdc.stdlib;
import core.sys.posix.dlfcn;

enum animalid{cow=7,chicken,horse};

struct animal{
    animalid id;
    string say;
    int age;
}
...
```

An algebraic datatype, a sumtype, a discriminated union, etc. And a 'fat pointer' is a just pointer to tagged data with some Data-Oriented Design implications about the data. Wikipedia has one good line about it:

A tagged union can be seen as the simplest kind of self-describing data format. The tag of the tagged union can be seen as the simplest kind of metadata.

With std.sumtype a 1:1 version of your code is a real pain, but your method would be more painful if the variants weren't so similar. std.sumtype's also less painful over time, refusing newanimal("idk"), having a compile-time exhaustiveness check. There are some rough edges like .each! not working here.

Skipping "do",

```d
import std;
import std.sumtype;

enum animalid {cow = 7, chicken, horse};
struct Cow { string say = "moo"; int age; }
struct Chicken { string say = "cluck"; int age; }
struct Horse { string say = "niah"; int age; }
alias Animal = SumType!(Cow, Chicken, Horse);

animalid id(Animal a) => a.match!(
    (Cow _) => animalid.cow,
    (Chicken _) => animalid.chicken,
    (Horse _) => animalid.horse,
);

int age(Animal a) => a.match!(
    (Cow a) => a.age,
    (Chicken a) => a.age,
    (Horse a) => a.age,
);

void speak(Animal a) => a.match!(
    (Cow a) => writeln("this cow says ", a.say),
    (Chicken a) => writeln("this chicken says ", a.say),
    (Horse a) => writeln("this horse says ", a.say),
);

int[animalid] maxage;

bool isdead(Animal a) => a.age > maxage[a.id];

void passtime(ref Animal a, int years) {
    a.match!(
        (ref Cow a) => a.age += years,
        (ref Chicken a) => a.age += years,
        (ref Horse a) => a.age += years,
    );
}

unittest {
    Animal[] foo;
    foo ~= Cow().Animal;
    foo ~= Chicken().Animal;
    foo ~= Horse().Animal;
    writeln(foo);
    foreach (ref a; foo) a.passtime(10);
    maxage[animalid.chicken] = 5;
    maxage[animalid.cow] = 15;
    maxage[animalid.horse] = 10;
    foo.map!isdead.writeln;
    maxage[animalid.chicken] = 500;
    foo.map!isdead.writeln;
}
```

std.sumtype's more similar to ML or Rust types. Ada and Nim feel a lot more like your method:

```nim
import std/[strformat, sequtils]

type
  AnimalKind = enum
    cow, chicken, horse
  Animal = object
    kind: AnimalKind
    say: string
    age: int = 0

# I typo'd "chicken" in this function and got confused when nothing died.
func newanimal(s: string): Animal =
  case s
  of "cow": Animal(kind: cow, say: "moo")
  of "chicken": Animal(kind: chicken, say: "cluck")
  else: Animal(kind: horse, say: "niah")

proc speak(a: Animal) = echo &"This {a.kind} says {a.say}"
var maxage: array[AnimalKind, int]
proc old(a: var Animal) = a.age += 10
proc isdead(a: Animal): bool = a.age > maxage[a.kind]

when defined(unittest):
  var foo: seq[Animal]
  foo.add newanimal("chicken")
  foo.add newanimal("cow")
  foo.add newanimal("idk")
  echo foo
  for a in foo: a.speak
  for a in foo.mitems: a.old
  maxage[chicken] = 5
  maxage[cow] = 15
  maxage[horse] = 10
  echo foo.mapIt(it.isdead)
  maxage[chicken] = 500
  echo foo.mapIt(it.isdead)
```

Reply via email to