-
Notifications
You must be signed in to change notification settings - Fork 0
Types
Types are part of generic system, you may use them to define which methods, fields, type variables you expect to be present in the input value, and/or to define the hierarchy of expected value.
type Sized {
val size: Int // either a size field, size() method or getSize() method.
}
type SizedSequence<T: Sized> {
iterator(): Iterator<T>
}
fun printSize<T: Sized>(sized: T) {
println(sized.size)
}
fun printSizedElements<T: SizedSequence>(seq: T) {
seq.forEach {
printSize(_) // _: Sized
}
}
class MyList<E> {
...
val size: Int
...
}
printSize(myList)
As Type:
val string = "well"
val sizedString = string as Sized { size = _.length() }
printSize(sizedString)
You could not use Any type as subject of a type checking, example:
fun serialize(type: Any): Json {
if (type is Sized) { // error: Any object could not be determined if is a `Sized` object.
return Json { "size": type.size }
} else {
return Json {}
}
}
Firefly compiler does not permit a check against an object of Any type because it is too less restrictive, it is almost like using a dynamic typing system. If you need a dynamic check you should use a dynamic type, like:
fun serialize(type: dynamic) Json {
if (type is Sized) {
return Json { "size": type.size }
} else {
return Json {}
}
}
Firefly compiler could optimize restrictive cases (and it means, faster resulting performance). Example:
class Car {
fun move...
fun remainingFuel...
fun health...
}
class Bus {
fun move...
fun remainingFuel...
fun health...
}
type Movable {
fun move(distance: Double): MoveResult where distance: Positive
}
type Motorized {
fun remainingFuel(): Double where return: Positive
fun health(): Double where return: InRange(0.0, 100.0)
}
fun move(object: Car|Bus|Movable) {
object.move(5.5)
}
fun health(object: Motorized): Double {
object.health()
}
In the first case, Firefly could create specialized versions of move which will be faster than using only Movable object, because that implies in type resolution during runtime and method checking.
In the second case, Firefly will only create specialized versions of move if types is used within same project (because firefly could analyze usages before compiling the code).
In Firefly, types are not directly implemented, so they does not really exists at the runtime. Because of this behavior, returning a type in a function will fail to compile, for example:
type Ord {
//...
}
class OrdWrapper {
fun value(): Ord // Fails to compile: could not concretize `Ord` type.
}
val ords: list<Ord>() // Fails to compile: could not concretize `Ord` type.
In these cases, you could define a concrete type to represent your type, for example:
type Ord = Int {
// ...
}
Note that, when concretized, the final type will be an Int, this means that your type must either have a function which returns an Int or implement concretize method, as the following:
type Ord = Int {
fun concretize(): Ord = 0
}
But you cannot revert this operation, when it is done, it is done, unless you have an unconcretize implementation.
Because of that, it may be hard to directly use type on lists, for Ord type for example, it does not have an unconcretize implementation, so you will be left with an int value, which may be meaningless for your code, to solve this, you could use Wrappers or Generic Types, the last one is the more recommended, for example:
fun hashAll<E: Hash>(list: List<E>): Hash {
//... your code
}
fun createListWithHashableTypes<E: Hash>() = list<E>()
// Or using wrapper, which I think that is really really really ... bad:
class HashWrapper<E: Hash> {
val value: E
}
fun createListWithHashableTypes() = list<HashWrapper<?>>()
This will allow you to store types that directly or indirectly implements the specified type, without leading to compilation errors.
Note that, even when using Generic Types, you could not provide the type itself to fill the generic type, so the following is not allowed:
val list = createListWithHashableTypes<Hash>()
Because, in this case, the Hash type could not be concretized (actually it can, to an Int value, but the operation cannot be reverted). You need to provide a concrete type in this case, it is the same as using a Function to fill generic parameter, for example:
val list = createListWithHashableTypes<fun(): Int>()
You cannot revert the operation after the function is applied.
TODO