= Deimos Jesse Brault 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 = [] for i in 0..100_000 do cats << Cat("meow") // creates a new Cat object in memory each iteration end let dogs: List = [] 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 ----