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 Collections 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 traits. 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!