0.6.0 - foreach, iterator protocol and generic supertypes
This release adds multiple bigger features that build on top of each other:
- wildcard type arguments
- generic/parametric supertypes
- iterator protocol
- foreach syntax
Generic/Parameters Supertypes
It is now possible to inherit from interfaces that have type parameters:
interface Iterable<T> {}
class List<T> : Iterable<T> {}
This might seem like a simlpe change on the surface, but it reached quite deep and enables a ton of possibilities.
A concept for containers and iteration
This release also contains decision for how emerge handles containers and iteration. I decided to go with the same core principles as the D language, adapted to fit emerges idioms.
The key types in emerge are: emerge.core.range.Iterable, emerge.core.range.InputRange<T> and all its subtypes.
Foreach
With a concept for iteration in place it is now also possible to build a foreach syntax. That has now been added to the language:
numbers = [1, 1, 2, 3, 5, 8, 13, 21]
foreach n in numbers {
StandardOut.put(n.toString())
StandardOut.putEndOfLine()
}
Foreach is basically syntax sugar, but the important bit is that it defines an iterator protocol, similar to how JavaScript does it, which revolves all around emerge.core.Iterable and emerge.core.InputRange. In the above example, the foreach desugars to this:
_hiddenRange: mut _ = numbers.asRange() // part one of the iterator protocol: Iterable.asRange
do {
n = try {
_hiddenRange.front()
} catch e {
// part two of the iterator protocol: InputRange.front(), plus using the exception to signal end-of-data
if e is EmptyRangeException {
break
} else {
throw e
}
}
StandardOut.put(n.toString())
StandardOut.putEndOfLine()
_hiddenRange.popFront() // part three of the iterator protocol: advancing to the next element
} while true
Wildcard type arguments
It is now also possible to specify * as the argument to a type parameter. The compiler will then figure out the bound of that parameter
and the * is treated as out Bound:
// before
val someIterable: Iterable<out Any>
// after, more concise and easier to refactor
val someIterable: Iterable<*>
Other improvements
As usual, this release also contains bug fixes, improves the clarity of various diagnostic messages and refactorings. I want to point out one of these: when you import a non-existing type, you'll only get an error for that import instead of a gazillion errors for all the times you've used it:
import foo.bar.DoesNotExist
fn frobnicate(p: DoesNotExist, x: DoesNotExist) -> DoesNotExist {
return if p.foo == x.foo p else x
}
Before, you would get 4 errors. One for the import and 3 for the usages. Now its only one.