148 lines
3.7 KiB
Plaintext
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
|
|
---- |