I haven’t written the formal docs of it yet, but one of the things I’m really looking forward to implementing in Beagle is type ignorance. Type ignorance is, as I would formally describe it, the description of a type’s known and unknown attributes. This means that you can specify how much of an object’s type you know, including whether it exists, aka nullability. I have created 4 (technically 5) different degrees of ignorance. These different degrees of ignorance get more and more ignorant the high you go. They are as follows:
-
First Degree: Nullability
This tells the compiler that you know what type an object is but you don’t know whether it will exist at any given time. This is an already widely known and used feature in several languages. This is often denoted as a ? sigil after a type annotation, like so (in Beagle syntax)
def val a: A? = null
-
Second Degree: Type Category
This tells the compiler that you don’t know what type an object is but you know what “type category” it belongs to, either a class or a struct. This allows you to perform more complex pattern matching on this object to perform what I call type elaboration (I’ll show this later). To declare something of being type ignorant to the second degree, using class as an example, is as follows:
def fun registerSomething(item: class?, size: Int){
}
You can further declare something as being a class but also nullable by appending a second ? sigil.
def fun registerSomething(item: class??, size: Int)
-
Third Degree: Pseudotypes
This tells the compiler that you don’t know what type it is but you know what kind of pseudotype it is. Pseudotypes are types that are only known at compiletime but are lost at runtime. This includes traits and abstract types. Traits are a way of abstracting compositions of structs. Abstract types allow you to define abstractions that can be implemented by both classes and structs. Abstract types get converted at compiletime to the appropriate abstract, either an interface (which is a type) or a trait (a pseudotype). This is what makes abstract types psuedotypes. The syntax looks about like this:
def trait T1{}
def type AT{}
//register takes an object that is a struct that 'comes with' trait T1.
def fun register(item: struct? with T1){}
//register takes an object of anything that implements abstract type AT
def fun register(item: type? of AT){}
-
Fourth Degree: Absolute Ignorance aka Opaque Types
Absolute ignorance means you don’t know anything about these types. These are equivalent to opaque types in C/C++. These are denoted by just a single ? sigil. This allows anything to be passed to it. This also allows you to elaborate as much as you want.
def fun register(item: ?){}
You can specify this object as being nullable, like mentioned before, with a second ?
def fun register(item: ??){}
This allows much cleaner bindings with C/C++ libraries, since lots of them will be using opaque types and void pointers.
Now moving on to type elaboration. It’s the utilization of pattern matching to elaborate on what type something is. It would be best to go through the layers for the most accurate elaboration in conjunction with clean and safe error handling.
An example:
def class A
def trait T1
def struct B with T1
def val classRegistry = ArrayList<class?>() //An ordered mutable set
def val structRegistry = Scalar<struct?>() //A mutable vector
def val registryOfA = ArrayList<A>()
def val registryOfB = Scalar<B>()
def val registryOfT1 = Scalar<struct? with T1>()
def fun register(item: ?): Result<Unit>{
match(item){
class? -> {
classRegistry.add(it)
match(it){
A -> registryOfA.add(it)
else -> Result.Error("Found a class but could not match a known concrete type")
}
}
struct? -> {
structRegistry.add(it)
match(it){
struct? with T1 -> {
registryOfT1.add(it)
match(it){
B -> registryOfB.add(it)
else -> Result.Error("Found a struct with trait T1 but could not match a known concrete type")
}
}
else -> Result.Error("Found a struct but could not match a known concrete type")
}
else -> Result.Error("Could not elaborate ignorant type")
}
}
This example doesn’t cover elaborating on abstract types but I’m sure you get the idea by now. I honestly cannot wait to get this feature implemented in Beagle. It’s gonna be really nice to have. This feature is still a WIP feature, but when I come across that road, I’ll definitely work out the kinks.