deimos-lang/doc/deimos.asciidoc
2026-03-12 18:41:41 -05:00

148 lines
3.7 KiB
Plaintext

= Deimos
Jesse Brault <jesse@jessebrault.com>
0.1.0
:toc:
== Language
=== Records
[source]
----
pub record Foo(bar: Int, baz: String) end
fn main()
let foo = Foo(1, "Hello")
println(foo.bar()) // 1
println(foo.baz()) // Hello
let foo2 = Foo(1, "Hello")
println(foo == foo2) // true
end
----
Records are essentially classes whose data members are all shallowly immutable. The data members of the constructor are
always inherently public; it is a syntax error to include the `pub` keyword.
Records inherently have methods named after the data fields to get the fields' values. It is an error to redeclare a
data field's method. Additionally, records inherently implement `Eq` and `Hash`, but each only if the constituent
members also implement those traits. *Records that do _not_ automatically implement `Eq` _and_ `Hash` must manually
implement the missing traits; it is an error to not do so.*
==== DVM/Performance Considerations
Records are interned and compared based on their data content, much like Strings (however, while Strings are primitive,
records are not). References to the record's shared data are cloned in the VM for all same-valued instances. To
illustrate:
[source]
----
class Cat(pub sound: String) end
record Dog(sound: String) end
fn main()
let cats: List<Cat> = []
for i in 0..100_000 do
cats << Cat("meow") // creates a new Cat object in memory each iteration
end
let dogs: List<Dog> = []
for i in 0..100_000 do
dogs << Dog("meow") // creates one Dog object in memory on first iteration,
// with 100_000 references stored in dogs list
end
end
----
==== Engineering Considerations
Records should be preferred against classes in most cases where the data of the class is all immutable, as this saves
memory and should result in faster execution times for data-heavy workloads.
==== Extern Records
Extern records signify native (Rust or otherwise) objects. They are declared with the `extern` keyword. However, unlike
plain Deimos records, all data-access methods, as well as the `Eq` and `Hash` traits' methods, must be implemented
natively, as the Deimos compiler has no method of knowing how to actually access the inner data.
[source]
----
extern record GameCharacter(name: String) end
----
== Compile Error Explanations
=== Not more than one constructor
Classes may have up to only one constructor. The following does not compile:
[source]
----
class Foo
ctor(bar: Int) end
ctor(baz: String) end // Error
end
----
=== Field must be initialized
All class fields must be initialized in their declaration, or in the constructor. It
is an error to leave a field uninitialized.
[source]
----
class Foo
bar: Int // Error: no initializer
end
class Bar
baz: Int // Error: no initializer
ctor()
// some other stuff
end
end
----
=== Use of field before initialization
A field _without_ a declared initializer (i.e., one present at its declaration, not the constructor) may not be read in
the initializer of any other field or constructor variable, as the language does not guarantee a specific ordering of
field initialization and cannot statically analyze field/variable dependencies.
[source]
----
class Foo
bar: Int
baz = bar // Error
end
class Bar
foo: Int
baz = fnWithArg(foo) // Error
end
class Baz
foo: Int
qux: String
ctor(baz: String)
qux = foo + baz // Error: attempt to read uninitialized foo
end
end
----
The exception to this is reading the field inside another field's initializer *only* when that expression is itself a
closure.
[source]
----
class Foo
bar: Int
baz = { bar } // OK
ctor(bar: Int)
self.bar = bar
end
end
----