the naming of types

the naming of types
Photo by Yana Yuzvenko / Unsplash

I've been skirting around a problem I introduced early on because I'm not sure what the best answer is. The naming of types.

I have noticed that in a lot of new indie languages they do things like declare primitives types all lower case: int8, uint, bool, etc. But then they ask their users to capitalise their own types: Person, Filename, StreetAddress.

I'd rather like all type naming to be the same whether it's a primitive type or a user type. Because of that I decided to name everything with lowercase and snake-style: person, filename, street-address.

The problem should be obvious already. Variable names often represent an abstract thing rather than a specific thing, eg: person instead of jane or filename instead of README.md.

If I have a type of person then I cannot also have a variable of person. Or.. can I?

Places you can use a type:
variable: type = value
variable: type
variable := (type | a, b, c, ...)
variable := {type | a, b, c, ...}
a-class := (a: type, b: type, ...)
a-block := [a: type → b: type | ... ]
a method | a: type → b: type [ ... ]

Unlike in Smalltalk-80 where you use the type to do instantiation by sending a message to the object, in STZ the class object might (probably) only exist at compile time.

Getting the type at runtime can be done by asking for the objects type much like you do in Smalltalk-80, eg: a-class := my-variable class. No where do you specifically need to name the class at runtime.

More often than not the meta-data a class can provide is useful at compile time rather than runtime. Especially since compile time runs an STZ interpreter allowing you to do very fancy things, such as dynamically creating new classes.

This leads to a curious pattern:

person: person = (name: 'jane')
person := (person | name: 'jane')
person: person

This is perfectly valid stz code. Is it a good idea? with syntax highlighting - sure why not. Without it, eeeh it's a bit confusing. One solution is to step back and use camel-case or pascal-case for types, or to have a suffix like -t or -type, -c, -class, etc.

But, I think, a little quierkiness is sometimes a fun thing in a programming language. Let's assume that syntax highlighting exists and will bold or italicise or change the colour of the type and therefore none of this is a problem when visually scanning the code.

person := (name: string,)
person: person
database-session
  read-one: 'select * from people where name = ?1'
  bind: ('jane',)
  into: &person

Which leaves the problem above as the last thing to solve. The naming of types. We cannot redefine person like this. It's also a little peculiar in that it doesn't fit with the syntax for declaring a method.

a method: b signature: c | a: some, b: type, c: information → ø [ ... ]

I would love a different symbol than | to divide this stuff. Perhaps a simple · would suffice. Some kind of answer required here that isn't immediately obvious. But moving on - we officially need a way to declare a type. Fortunately it's probably as simple as doing person (name: string,)

person (name: string,)
person: person
database-session
  read-one: 'select * from people where name = ?1'
  bind: ('jane',)
  into: &person

And there we have it. It's been a bit of a journey but we officially have a top-level syntax for declaring a class. While we're at it, enums:

cardinals (uint | north = 1, east, south, west)

What about type unions and combinations? That does get a little bit trickier. Combinations is easy at least using list syntax:

combined-class (...class-a, ...class-b)

That just leaves a need for a union syntax. We could use `my-union {...class-a, ...class-b} but that kind of syntactical overload doesn't work for my brain. {} is for executing code on an new instance of a type, not for making unions. The two don't equivocate. So let's add a syntax:

my-class (floatingstuff: float64) + (intstuff: int64)

That isn't great. We could specify that we're making a union. After all, the map we provide to make a class is a special type of class so why not have a special type of union.

// the same
my-class (name: string,)
my-class (class | name: string,)

// no shortcut
my-union (union | byte-name: string8, unicode-name: string32)

It's not quite as elegant as using * and + to combinatorially create new types. But it is at least clear what's going on. We could, for completeness, also add enum of: uint (for example) as a special type when creating enumeration lists.

This now makes the top-level syntax look roughly like this:

type-name (enum | ...name... )
type-name (class | ...name:type... )
type-name (union | ...name:type... )
signature | type [ ...code... ]
...code...