= 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
----