stz memory-address, reference, and array
C pointers are ... a thing. Not a good thing. But a thing. The name 'pointer' is also ... a thing. What we really have is an address in memory and we get or set things from it. We don't know how much of a thing we have in memory but we do at least know its type.
So let's begin with memory-address. As the C equivalent of a pointer it holds on an integer appropriate for the platform pointer type.
memory-address := distinct: u64 // yes this is a little naive but we'll roll with it
In other modern languages they have a thing called a Slice. A Slice is a slice of memory. A starting address and a length. Sometimes these are called 'fat pointers' too. Slice is an okay-name. But really it's an array. If we define the array as pointing to a place in memory, knowing its element type, and how many there are then array is a slice.
Which brings us to reference. A reference is a single element array. It doesn't act like an array - instead it acts like a proxy for the one and only element. That element can also be undefined which can be conveniently encoded as a memory-address of 0.
This is a common optimisation in languages with Maybe or Option. The specialisation for the Maybe or Option for a pointer being undefined is a pointer value of 0.
Since we're likely to have tagged unions we should batten done some core concepts with them. Probably the first is a choice. A choice between ClassA or ClassB. The second one is a nullable reference which for now we'll call maybe.
choice of: A or: B
maybe an: A
Maybe is quite useful. Many boolean type questions go away because you don't have to check if the value is nil/null/0/empty. Instead you can use then: and else: on the value immediately, eg:
thing := get-the-thing
thing else: [ no-thing ]
thing then: [ thing | do-the-thing: thing ] // thing is shadow unwrapped thing
This is where we start looking at monads and going Ooo Aaa with the composition function. While-ever may be is not nil it can be passed on to the next function, otherwise it falls through to the final else statement.
a_div_b_times_two := [ a, b | a / b ] • [ c | c * 2 ] else: [ uh-oh ]
answer := a_div_b_times_two evaluate: {1, 0}
Using the monadic approach here either we get back the answer or we get back whatever uh-oh is; but the important point is we've auto-handled the fail state.
Oh look, some new syntax. Literal array. Each element is a statement to be evaluated. Ideally at compile time. Since the first code block takes two parameters we need to pass in two values. It outputs only one value so it can be composed with the next code block which takes one parameter and outputs one parameter.
That's enough side tracking with monads for now. Let's get back to what we came here to talk about - memory-address, array, and reference.
Since most everything that's on the heap is going to ultimately have a memory-address it turns out to be a very important class. But just as important is array:
array := parameter: {of: class} class: {address: memory-address, length: uint64}
fixed-array := parameter: {of: class, length: u64} class: {address: memory-address}
In fact array is so important we might need to give it some special syntax too. Perhaps we'll come back to that. On to reference:
reference := parameter: {of: class} distinct: [ fixed-array of: class length: 1 ]
Note: this is not the actual syntax for defining parametric types. In reality there shouldn't be a syntax for it. They should simply be methods.
fixed-array-of:length: := [ element-class: class, length: uint64 | ... magic-sauce-here ... ]
reference-of := [ element-class: class | fixed-array-of: element-class length: 1 ]
Uh oH! ZOOM AND ENHANCE: of:length: := [ we have a syntactical problem here. The declaration of a method looks terrible. We might have to figure that out in the next blog post...