Interview with Magnus Madsen about the Flix Programming Language

Flix, an open-source programming language inspired by many programming languages, enables developers to write code in a functional, imperative or logic style. Flix looks like Scala, uses a type system based on Hindley-Milner and a concurrency model inspired by Go. The JVM language supports unique features such as the polymorphic effect system and Datalog constraints.

Flix programs are compiled to JVM bytecode and developers can use the Flix Visual Studio Code extension or evaluate the language by using the online playground.

The community develops the language based on a set of principles such as: no null value, private by default, no reflection, separation of pure and impure code, correctness over performance and no global state.

The following main function is considered impure as the println function has a side effect. The Flix compiler keeps track of the purity of each expression and guarantees that a pure expression doesn’t have side effects.

def main(_args: Array[String]): Int32 & Impure =
    println("Hello world!");
    0 // exit code

The Flix language supports Polymorphic Effects making it possible to distinguish between pure functional programs and programs with side effects. Functions are pure by default, or can explicitly be marked as pure:

def add(x: Int32, y: Int32): Int32 & Pure  = 
    x + y;

Functions with side effects can be marked explicitly as impure:

def main(_args: Array[String]): Int32 & Impure =
println(add(21, 21));
0 // exit code

The compiler displays an error Impure function declared as pure whenever side effects are used in an explicitly marked Pure function:

def add(x: Int32, y: Int32): Int32 & Pure  = 
    x + y;

The separation of pure and impure code allows developers to reason about pure functions as if they are mathematical functions without side effects.

Datalog, a declarative logic programming language, may be seen as a query language such as SQL, but more advanced. Flix supports Datalog as a first-class citizen making it possible to use Datalog constraints as function arguments, returned from functions and stored in data structures. Flix may be used with Datalog to express fixpoint problems such as determining the ancestors:

def getParents(): # r  = #
    ParentOf("Mother", "GrandMother").
    ParentOf("Granddaughter", "Mother").
    ParentOf("Grandson", "Mother").

def withAncestors(): # ParentOf(String, String), 
                        AncestorOf(String, String)  = #
    AncestorOf(x, y) :- ParentOf(x, y).
    AncestorOf(x, z) :- AncestorOf(x, y), AncestorOf(y, z).

def main(_args: Array[String]): Int32 & Impure =
    query getParents(), withAncestors() 
        select (x, y) from AncestorOf(x, y) |> println;

This displays the following results:

[(Granddaughter, GrandMother), (Granddaughter, Mother), 
    (Grandson, GrandMother), (Grandson, Mother), (Mother, GrandMother)]

Flix provides built-in support for tuples and records as well as algebraic data types and pattern matching:

enum Shape 
    case Circle(Int32),          // circle radius
    case Square(Int32),          // side length
    case Rectangle(Int32, Int32) // height and width

def area(s: Shape): Int32 = match s 
    case Circle(r)       => 3 * (r * r)
    case Square(w)       => w * w
    case Rectangle(h, w) => h * 
Read More