Update Summary: v0.7.0 to v0.16.0
It’s been a while since the last update on Dyvil, back then it was in version 0.6.0
. Now, we already reached milestone 0.
16
.0
, which is the equivalent of 10 major releases! Anyway, away from the numbers, let’s get talking about features. The following are notable:
- The Dyvil Property Format
- Improvements to Closures and Partially Applied Functions
- References
- Traits and Super Type Arguments
- Initializer Blocks
- Reified Type Parameters
- Pattern Matching
The Dyvil Property Format
The Dyvil Property Format is a text format specifically designed and created for configuration files. A typical DPF file might look like this:
sources {
dir = "./src"
include = [ "*.dyv" ]
exclude = [ "*.java", "*.scala" ]
}
output.dir = "./output"
libraries = [ "./libs/dyvil-property-format.jar" ]
jar {
name = "myprogram"
vendor = "clashsoft"
version = "v0.1.0"
main {
class = "com.clashsoft.myprogram.Main"
args = [ "--verbose", "-i" ]
}
}
As you can see, DPF files consist of a number of nodes { }
(or blocks) containing properties = value
. This particular example could be configuration file for the Dyvil compiler. You can learn more about the Dyvil Property Format on the documentation page.
Closures and Partially Applied Functions
Previously, closures (or lambda expressions) where always defined with an =>
arrow:
any => void print = x => println x
(int, int) => int add = (x, y) => x + y
Over the last few updates, a number of more visually pleasing and concise ways have been added to represent lambda expressions and call higher-order functions. For example, the Predef.repeat(int, => void)
method is defined like this:
void repeat(int n, => void f) = for (int i : 1 .. n) f()
And can be called with a trailing block of code:
repeat(3) {
println "Hey"
}
This almost makes it look like repeat is a special kind of statement, while it’s in fact nothing special at all!
The next interesting higher-order function is the foldLeft
method that all Collection
s provide. First, we create a list:
List[int] list = [ 1, 2, 3, 4, 5 ]
Then, we call the list’s foldLeft
using a so-called partially applied function, denoted using the _
symbol:
int result = list.foldLeft(0, _ + _) // result = 15
This is the equivalent of the following, using lambda expressions:
int result = list.foldLeft(0, (carry, element) => carry + element)
Alternatively, we can use a trailing block closure:
int result = list.foldLeft(0) { $0 + $1 }
In this case, $0
and $1
are the implicit parameter names.
References
Many programming languages have a notion of references (or pointers in low-level languages like C). They effectively allow you to pass fields (properties, attributes) or local variables to a method, which will then proceed to modify their value. Dyvil also allows you to do this in a clean and safe manner. In the following code snippet, we declare a method inc
that takes a reference to an int
and increments the value of the field that reference points to.
void inc(int& i) = i.set(i.get + 1)
int i = 0
inc(&i) // read '&i' as 'reference to i'
println i // prints '1'
The above example uses explicit references, which require an explicit usage of the reference operator &
at the use site. However, we can also make it implicit by using by-reference parameters:
void int(var int i) = i++ // get and set are called implicitly
inc i // works as above
Traits and Super Arguments
Another new concept in Dyvil that borrowed it’s name from Scala is trait
s. Traits are effectively interfaces with default methods that are not required to be implemented by sub-classes.
trait Named
{
String name() = "unnamed"
}
object UnnamedPerson implements Named
UnnamedPerson unnamed = UnnamedPerson.instance
println unnamed.name // prints 'unnamed'
Super Arguments are a new way to declare super constructor arguments without introducing too much boilerplate. What previously looked like this:
class Person(String name)
class Student extends Person
{
private String email
public init(String name, String email)
{
super.init(name)
this.email = email
}
}
Can now be reduced to just two lines of code:
class Person(String name)
class Student(String name, String email) extends Person(name)
Initializer Blocks
Initializer Blocks are effectively the same as in Java, with a minor syntactic difference. What in Java look like this:
class Foo
{
String s;
{
System.out.println("Constructor Called")
s = "abc";
}
}
Works in Dyvil almost as easily:
class Foo
{
String s
init
{
println "Constructor Called"
s = "abc"
}
}
The way initializers work stays the same:
Foo foo = new Foo // prints "Constructor Called"
println foo.s // prints 'abc'
Reified Type Parameters
Dyvil now allows you to define method Type Parameters that are available at runtime within the body of the method.
Class[T] toClass[@Reified T]() = class T // like T.class in Java, but valid
println Test.[String]toClass // prints 'class java.lang.String'
This also works for more complex (generic) types:
Type[T] toType[@Reified T]() = type T
println Test.[Map[String, List[String]]toType
// prints 'dyvil.collection.Map[java.lang.String,
// dyvil.collection.List[java.lang.String]]
Pattern Matching
Pattern Matching is an extremely useful feature in functional programming languages, it has also been worked on throughly in Dyvil. Let’s take a look at a classic example, decomposing an optional value:
Option[int] option = Some(1)
option match {
case Some(int i) => println i
case None => println "No value for you"
}
The match
statement is the replacement for the traditional switch
in Java - but it is much more powerful, being able to decompose complicated compound values:
(int, (long, double)) tuple = (1, (2L, Math.PI)) // Tuple[int, Tuple[long, double]]
tuple match {
case (_, (long l, double d)) => println "Long Value: \(l), Double Value: \(d)"
case _ => println "unknown value"
}
Here, _
represents a wildcard pattern that will match any value, while long l
and double d
are binding patterns that bind values to variable names.
You may also want to simply match
any int
value - in which case Dyvil provides you with multiple options:
int value = 7
value match {
case 0 => println "zero"
case 1 => println "one"
case 2 | 3 | 5 => println "prime"
case 7 => println "found it"
case _ => println "invalid"
}
This example uses or
patterns, used to match any of the provided int
literal patterns.
Conclusion
As you can see, a lot of things have been going on on the Dyvil Project. Apart from all there features, many bugs have also been squashed and performance improvements have been made to make sure the language runs a stably and smootly as possible. If you want to test it out, grab the dyvil-v*.jar
over at the GitHub Releases Page and run it with your favorite terminal. The Dyvil REPL will allow you to type an expression or declaration and see what happens.
Have fun hacking!