stz unbound variables
Breaking down the class definition we have a problem:
list = { class |
parameters: (of: object-t,),
constants: (element-class: of,),
traits: (sequenceable-t of: element-class,),
variables: (
elements: &array of: element-class,
length: uint,
allocator: &memory-allocator,) }
The constants section references a variable that doesn't exist. The traits section references a variable that doesn't exist and the variables section references variables that don't exist.
One approach might be to revisit using methods to create classes. The idea behind the current code is it would generate the of: method for you which would create the actual class variant using the traits and variables.
If it can do that then we must be able to do that too. It should be a short-hand for a long hand. Let's try and write it out long-hand and see if it makes sense.
list-t = { class | }
[ list-t-class of: element-class
| list-t class, object-t -> class
| { class
| traits: (list-t, sequenceable-t of: element-class,)
variables: (
elements: &array of: element-class,
length: uint,
allocator: &memory-allocator,) } ]
Actually that's not too painful at all. It was one method that makes a class joining the trait groups for list-t (abstract) and sequenceable-t (also abstract). We even got rid of the silly of variable and the need for constants:.
[ list add: element
| &!list-t, object-t -> ø
| ... ]
[ list remove: element
| &!list-t, object-t -> ø
| ... ]
We might have lost a little something here. Instead of the second argument being an element-class we've left it wibbly with just object-t. Is this a problem? Probably. Let's fix it:
[ list add: element
| &!list-t of: element, element -> ø
| ... ]
[ list remove: element
| &!list-t of: element, element -> ø
| ... ]
Much better. Let's see if the rest of the hierarchy also makes sense:
sequenceable-t = { trait | }
[ sequenceable-t-class of: element-class
| sequenceable-t class, object-t,
| { class
| traits: ( collection-t of: element-class, ) } ]
/* accessing */
[ sequence first
| sequenceable-t of: element-class -> element-class
| sequence[0] ]
Hmm. That's a problem. We haven't defined element-class outside of the of: method. The approach works for for add: and remove: because the parameter gives us the type information we need. But here we're trying to return something from our sequenceable-t and we haven't the slightest idea what it would be.
If we state that the first parameter sequence must be a sequence-t then we aren't specifying we want the variant version – element-class does not exist. Because of the nature of the signature we cannot specify we want the variant version because we don't have enough type information.
We could specify that a sequenceable-t requires an element-class and the class that uses the trait will have to supply it:
list-t = { class | }
[ list-t-class of: element-class
| list-t class, object-t -> class
| { class
| traits: (list-t, sequenceable-t of: element-class,)
constants: (element-class: element-class,)
variables: (
elements: &array of: element-class,
length: uint,
allocator: &memory-allocator,) } ]
sequenceable-t = { trait | traits: (collection-t,) }
collection-t = { trait |
requires: (
[ collection element-class | collection-t -> object_t ],
[ collection length | collection-t -> length ],
[ collection[index] | collection-t, uint -> element-class ] ) }
/* accessing */
[ sequence first
| sequenceable-t -> sequenceable-t element-class
| sequence[0] ]
The breakdown here is that a list is a real implementation, a sequenceable has an order to it and a collection doesn't. You cannot ask a collection for its first thing because that concept doesn't exist. You can as a sequenceable for its first thing however.
In this case sequenceable-t only has requirements that match collection-t so it directly 'inherits' the requirements from collection-t and therefore collection-t methods can take a *list-t* parameter.
The sequenceable-t must implement element-class which isn't quite what we want. We want to be able to ask the type for its element-class. That's a compile time question rather than a runtime question.
collection-t = { trait |
requires: (
[ collection-t-class element-class | collection-t class -> object_t ],
[ collection length | collection-t -> length ],
[ collection[index] | collection-t, uint -> element-class ] ) }
We've used some serious meta programming capabilities a few times in this blog post that rely heavily on the assumption that you, the reader, know Smalltalk. That's concerning to me because it suggests may be things have become too complex.
We're passing around types and the type of the type needs to be specifiable. For now I've been sending class to the class to get the class of the class. This kinda sucks but is also brilliant. The design of Smalltalk's lowest levels are inspired.
May be there's a better way. May be there isn't. We're left with unanswered questions once more. Fewer unanswered questions at the very least.