238 lines
6.3 KiB
Markdown
238 lines
6.3 KiB
Markdown
# OO implementation in Lyng
|
|
|
|
## Declaration
|
|
|
|
The class clause looks like
|
|
|
|
class Point(x,y)
|
|
assert( Point is Class )
|
|
>>> void
|
|
|
|
It creates new `Class` with two fields. Here is the more practical sample:
|
|
|
|
class Point(x,y) {
|
|
fun length() { sqrt(x*x + y*y) }
|
|
}
|
|
|
|
val p = Point(3,4)
|
|
assert(p is Point)
|
|
assertEquals(5, p.length())
|
|
|
|
// we can access the fields:
|
|
assert( p.x == 3 )
|
|
assert( p.y == 4 )
|
|
|
|
// we can assign new values to fields:
|
|
p.x = 1
|
|
p.y = 1
|
|
assertEquals(sqrt(2), p.length())
|
|
>>> void
|
|
|
|
|
|
Let's see in details. The statement `class Point(x,y)` creates a class,
|
|
with two field, which are mutable and publicly visible.`(x,y)` here
|
|
is the [argument list], same as when defining a function. All together creates a class with
|
|
a _constructor_ that requires two parameters for fields. So when creating it with
|
|
`Point(10, 20)` we say _calling Point constructor_ with these parameters.
|
|
|
|
Form now on `Point` is a class, it's type is `Class`, and we can create instances with it as in the
|
|
example above.
|
|
|
|
Class point has a _method_, or a _member function_ `length()` that uses its _fields_ `x` and `y` to
|
|
calculate the magnitude. Length is called
|
|
|
|
### default values in constructor
|
|
|
|
Constructor arguments are the same as function arguments except visibility
|
|
statements discussed later, there could be default values, ellipsis, etc.
|
|
|
|
class Point(x=0,y=0)
|
|
val p = Point()
|
|
assert( p.x == 0 && p.y == 0 )
|
|
>>> void
|
|
|
|
## Methods
|
|
|
|
Functions defined inside a class body are methods, and unless declared
|
|
`private` are available to be called from outside the class:
|
|
|
|
class Point(x,y) {
|
|
// public method declaration:
|
|
fun length() { sqrt(d2()) }
|
|
|
|
// private method:
|
|
private fun d2() {x*x + y*y}
|
|
}
|
|
val p = Point(3,4)
|
|
// private called from inside public: OK
|
|
assertEquals( 5, p.length() )
|
|
// but us not available directly
|
|
assertThrows { p.d2() }
|
|
void
|
|
>>> void
|
|
|
|
## fields and visibility
|
|
|
|
It is possible to add non-constructor fields:
|
|
|
|
class Point(x,y) {
|
|
fun length() { sqrt(x*x + y*y) }
|
|
|
|
// set at construction time:
|
|
val initialLength = length()
|
|
}
|
|
val p = Point(3,4)
|
|
p.x = 3
|
|
p.y = 0
|
|
assertEquals( 3, p.length() )
|
|
// but initial length could not be changed after as declard val:
|
|
assert( p.initialLength == 5 )
|
|
>>> void
|
|
|
|
### Mutable fields
|
|
|
|
Are declared with var
|
|
|
|
class Point(x,y) {
|
|
var isSpecial = false
|
|
}
|
|
val p = Point(0,0)
|
|
assert( p.isSpecial == false )
|
|
|
|
p.isSpecial = true
|
|
assert( p.isSpecial == true )
|
|
>>> void
|
|
|
|
### Private fields
|
|
|
|
Private fields are visible only _inside the class instance_:
|
|
|
|
class SecretCounter {
|
|
private var count = 0
|
|
|
|
fun increment() {
|
|
count++
|
|
void // hide counter
|
|
}
|
|
|
|
fun isEnough() {
|
|
count > 10
|
|
}
|
|
}
|
|
val c = SecretCounter()
|
|
assert( c.isEnough() == false )
|
|
assert( c.increment() == void )
|
|
for( i in 0..10 ) c.increment()
|
|
assert( c.isEnough() )
|
|
|
|
// but the count is not available outside:
|
|
assertThrows { c.count }
|
|
void
|
|
>>> void
|
|
|
|
It is possible to provide private constructor parameters so they can be
|
|
set at construction but not available outside the class:
|
|
|
|
class SecretCounter(private var count = 0) {
|
|
// ...
|
|
}
|
|
val c = SecretCounter(10)
|
|
assertThrows { c.count }
|
|
void
|
|
>>> void
|
|
|
|
## Default class methods
|
|
|
|
In many cases it is necessary to implement custom comparison and `toString`, still
|
|
each class is provided with default implementations:
|
|
|
|
- default toString outputs class name and its _public_ fields.
|
|
- default comparison compares compares all public fields in order of appearance.
|
|
|
|
For example, for our class Point:
|
|
|
|
class Point(x,y)
|
|
assert( Point(1,2) == Point(1,2) )
|
|
assert( Point(1,2) !== Point(1,2) )
|
|
assert( Point(1,2) != Point(1,3) )
|
|
assert( Point(1,2) < Point(2,2) )
|
|
assert( Point(1,2) < Point(1,3) )
|
|
Point(1,1+1)
|
|
>>> Point(x=1,y=2)
|
|
|
|
# Theory
|
|
|
|
## Basic principles:
|
|
|
|
- Everything is an instance of some class
|
|
- Every class except Obj has at least one parent
|
|
- Obj has no parents and is the root of the hierarchy
|
|
- instance has member fields and member functions
|
|
- Every class has hclass members and class functions, or companion ones, are these of the base class.
|
|
- every class has _type_ which is an instances of ObjClass
|
|
- ObjClass sole parent is Obj
|
|
- ObjClass contains code for instance methods, class fields, hierarchy information.
|
|
- Class information is also scoped.
|
|
- We acoid imported classes duplication using packages and import caching, so the same imported module is the same object in all its classes.
|
|
|
|
## Instances
|
|
|
|
Result of executing of any expression or statement in the Lyng is the object that
|
|
inherits `Obj`, but is not `Obj`. For example it could be Int, void, null, real, string, bool, etc.
|
|
|
|
This means whatever expression returns or the variable holds, is the first-class
|
|
object, no differenes. For example:
|
|
|
|
1.67.roundToInt()
|
|
1>>> 2
|
|
|
|
Here, instance method of the real object, created from literal `1.67` is called.
|
|
|
|
## Instance class
|
|
|
|
Everything can be classified, and classes could be tested for equivalence:
|
|
|
|
3.14::class
|
|
1>>> Real
|
|
|
|
Class is the object, naturally, with class:
|
|
|
|
3.14::class::class
|
|
1>>> Class
|
|
|
|
Classes can be compared:
|
|
|
|
assert(1.21::class == Math.PI::class)
|
|
assert(3.14::class != 1::class)
|
|
assert(π::class == Real)
|
|
π::class
|
|
>>> Real
|
|
|
|
Note `Real` class: it is global variable for Real class; there are such class instances for all built-in types:
|
|
|
|
assert("Hello"::class == String)
|
|
assert(1970::class == Int)
|
|
assert(true::class == Bool)
|
|
assert('$'::class == Char)
|
|
>>> void
|
|
|
|
More complex is singleton classes, because you don't need to compare their class
|
|
instances and generally don't need them at all, these are normally just Obj:
|
|
|
|
null::class
|
|
>>> Obj
|
|
|
|
At this time, `Obj` can't be accessed as a class.
|
|
|
|
|
|
### Methods in-depth
|
|
|
|
Regular methods are called on instances as usual `instance.method()`. The method resolution order is
|
|
|
|
1. this instance methods;
|
|
2. parents method: no guarantee but we enumerate parents in order of appearance;
|
|
3. possible extension methods (scoped)
|
|
|
|
TBD
|
|
|
|
[argument list](declaring_arguments.md) |