Beagle is being designed to allow you to choose how your code is compiled and how objects are treated at runtime. Let’s say for example, you’re building for an embedded systems (I have a fitlet2 setup in my office for a work project) that has limited hardware, you might wanna think about the efficiency and size of your objects in your program. So in the case you might not wanna maintain inheritance information at runtime as that can expensive to process by the CPU and costs the RAM quite a bit. Also inheritance related polymorphism can be really expensive too. So Beagle will let you create structs
instead of classes
by choice. Ofc you can still create classes. You can abstract away structs using Rust-like traits.
def trait Pingable{
def fun ping()
}
def struct Device with Pingable{
def impl fun ping(){
TODO()
}
}
You will also be able to abstract classes with interfaces like a lot of other oop languages. There will also be a common abstraction that doesn’t care what implements it as long as it gets implemented.
def type Iterable<T>{
def val entries: [T]
def fun forEach(block: (T)->Unit)
}
def class List<T>: Iterable<T>{
def impl val entries = [None; T]
def impl forEach(block: (T)->Unit){
TODO()
}
}
def struct Vector<T> with Iterable<T>{
def impl val entries = [None; T]
def impl forEach(block: (T)->Unit){
TODO()
}
}
The compiler will convert the abstract type to whatever kind of abstraction it needs to be for implementation, so interfaces for classes, and traits for structs.
This becomes essential when I have a multiplatform application that has slightly different hardware based implementations. With the slick module system, you can have a structure like this:
root
|- src
|- x86
|- windows
|- mod.bg
|- build.bg
|- darwin
|- mod.bg
|- build.bg
|- linux
|- mod.bg
|- build.bg
|- arm
|- pi
|- mod.bg
|- build.bg
|- build.bg
I know that the def
keyword all over the place seems a bit much but this is because of a feature I am designing called def macros
. def macros
will let you create a thin layer between an API and its use that is forward compatible and more maintainable. So let’s say I have some kind of game engine framework that provides an Entity API with a registry system. So I want to create a new entity called Player. This is how it may look:
def struct Player(def val world: World) with Entity(world), Tickable{
def impl fun onSpawn(){
TODO()
}
def impl fun onTick(partialTicks: Int64){
TODO()
}
def impl fun onDespawn(){
TODO()
}
}
That’s kinda messy and if something happens to the overal architecture of the API, this setup is broken. def macros
Kinda fix this. Lemme show you
def tickable entity Player{
onTick{
TODO()
}
onSpawn{
TODO()
}
onDespawn{
TODO()
}
}
This makes maintainability much more concise and managed through the def macros which is provided by the API. Using an API like this would be so great in my opinion.
I’ve mentioned before that I plan to have a working demo by the end of the year and with how well kotlinx-llvm is going, I think that is completely achievable.