Coding 2022-08-25
I figured I'd keep on thinking about the programming language concept I sort of mentioned last night. Before I do anything else, I want to name it so I can have something convenient to refer to, but I don't want to commit to a name yet. As such, I'll use a working title that practically demands to be replaced, once a suitable replacement suggests itself:
Name Already Been Thought Of, or NABTO. This is an allusion to trying to come up with some punchy, memorable name, and oops, it's already in use. With its somewhat unusual combination of different points of articulation and voicing, NABTO is the programmer art of names, which is what I need, psychologically, to get on with it.
So, one of the major inspirations for NABTO is Python. I use Python a lot, and I wonder what it would be like to use a language that is like Python, but, at a few points, does things the way I would do them, seeing how they worked out in Python.
Two big pain points for me in Python are inheritance and __init__ methods. As of the last few years, when I'm left to my own devices, I use inheritance rarely, and explicit __init__ methods less. Here are the statistics from the current MOTR codebase:
- Classes that inherit:
- From typing.Generic: 20
- From typing.Protocol: 16
- From enum.Enum: 3
- Internally: 9
- From third-party code: 3
- From an exception: 2
- Not at all: 41 (+2 required to exist by third-party code, but with no inheritance)
- Explicit definitions of __init__: 0 (thank you, attrs)
So, from looking over these, we have 39 instances of inheriting from the standard library to get specific functionality, most of the internal ones and the exceptions are cases of ontological inheritance, and the third-party stuff is to hook into third-party functionality by inheriting a bunch of functionality.
Ontological inheritance is one of three kinds laid out in this post. The others are abstract data type inheritance and implementation inheritance. For my purposes, I'm going to separately consider inheriting the implementation from a concrete class and inheriting the implementation from an abstract class. I like this distinction because I generally distrust the former, and I apparently do the latter a lot.
So, if I want NABTO to reflect my own ideas about how to code, how do these different types of inheritance look?
Suppose we consider different types of inheritance to be associated with different types of type.
We can have concrete types that represent some data in memory and a set of associated behaviors. We can have ontological types that represent open or closed sets of values. We can have abstract data types that represent the contracts that a value upholds. We can have mixin types that provide implementations of behavior without reference to state.
As to how these types can relate to each other:
- concrete types can have their instances point to other values, which will also be instances of concrete types.
- concrete types can be added to open ontological types; multiple inheritance imposes no constraints on ordering.
- concrete types can implement the contracts of abstract data types; multiple inheritance imposes no constraints on ordering.
- concrete types can inherit from mixin types; multiple inheritance imposes constraints on ordering, and I think the safe bet is C3.
- ontological types can relate to each other, and I'm not sure they should relate to anything else?
- abstract data types can require that their operations take values of specific concrete types, or that conform to other abstract data types, but I don't know if it makes sense for them to care about ontological types or mixin types; they can also inherit from each other to express relations like "a sequence is also a container" or whatever.
- mixin types can inherit from each other, and their methods can specify concrete types, or ideally abstract data types, in their signatures.
Right, signatures. I kind of want to see how gradual typing looks when it isn't bolted on after-the-fact. Maybe there are languages out there that are trying that, but after all of the languages I name-checked yesterday, I feel a little researched-out.
That off-hand reference I made to open or closed ontological types earlier made me realize something. Exception handlers in Python are like match statements that only work on instances of a particular type, passing through specific mechanisms. I'm really interested in Koka's ideas about minimalism; seeing them in action caused some ideas that have been kicking around in my head for over a decade to make sense, so let's see if anything inspires me there...
First off, I had some ideas for using one type like another type. Like, concrete types and abstract data types could be used as ontological types for some purposes, and a concrete type could be used as an abstract data type. My gut feeling is that using concrete types like that is likely to be a problem a lot of the time, but I think experience is worth more than feelings, so I think it should be allowed, but warned against. That way, if someone really needs it, they've got it.
(This sort of clarifies some of the uses of concrete types, above. Actually, it's a concrete type being interpreted as an abstract data type.)
It's getting late, and this post is apparently quite long, so I'm just going to spitball some thoughts about how different types of types could interact with match constructs.
- Only concrete types could perform destructuring match, since the other types don't specify a structure. I haven't gone over the aspects of concrete types that should make destructuring possible.
- Ontological types could check for membership, which is straightforward enough.
- I'm not clear on what abstract data types or mixin types could do in the context of a match.
After thinking about this a little more, my feeling is that concrete types should handle their own destructuring, so the language- or library- level matches should deal with ontological types.
In any case, this doesn't quite feel fully baked, but I want to be done with writing tonight. I think what I need to do is put together a Sphinx project for documenting how I think this all should work, before I try to prototype it.
Good night.