Flix (programming Language)
   HOME

TheInfoList



OR:

Flix is a
functional Functional may refer to: * Movements in architecture: ** Functionalism (architecture) ** Form follows function * Functional group, combination of atoms within molecules * Medical conditions without currently visible organic basis: ** Functional sy ...
, imperative, and
logic Logic is the study of correct reasoning. It includes both formal and informal logic. Formal logic is the science of deductively valid inferences or of logical truths. It is a formal science investigating how conclusions follow from premises ...
programming language A programming language is a system of notation for writing computer programs. Most programming languages are text-based formal languages, but they may also be graphical. They are a kind of computer language. The description of a programming ...
developed at
Aarhus University Aarhus University ( da, Aarhus Universitet, abbreviated AU) is a public research university with its main campus located in Aarhus, Denmark. It is the second largest and second oldest university in Denmark. The university is part of the Coimbra Gr ...
, with funding from the
Independent Research Fund Denmark The Independent Research Fund Denmark, until 2017 known as the Danish Council for Independent Research (Danish: ''Danmarks Frie Forskningsfond'', formerly ''Det Frie Forskningsråd''; ''DFF'') of Denmark funds basic research and gives advice to gove ...
, and by a community of
open source Open source is source code that is made freely available for possible modification and redistribution. Products include permission to use the source code, design documents, or content of the product. The open-source model is a decentralized sof ...
contributors. The Flix language supports
algebraic data types In computer programming, especially functional programming and type theory, an algebraic data type (ADT) is a kind of composite type, i.e., a type formed by combining other types. Two common classes of algebraic types are product types (i.e., ...
,
pattern matching In computer science, pattern matching is the act of checking a given sequence of tokens for the presence of the constituents of some pattern. In contrast to pattern recognition, the match usually has to be exact: "either it will or will not be ...
,
parametric polymorphism In programming languages and type theory, parametric polymorphism allows a single piece of code to be given a "generic" type, using variables in place of actual types, and then instantiated with particular types as needed. Parametrically polymorph ...
,
currying In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument. For example, currying a function f that ...
,
higher-order function In mathematics and computer science, a higher-order function (HOF) is a function that does at least one of the following: * takes one or more functions as arguments (i.e. a procedural parameter, which is a parameter of a procedure that is itself ...
s,
extensible records Extensibility is a software engineering and systems design principle that provides for future growth. Extensibility is a measure of the ability to extend a system and the level of effort required to implement the extension. Extensions can be t ...
, channel and process-based concurrency, and
tail call elimination In computer science, a tail call is a subroutine call performed as the final action of a procedure. If the target of a tail is the same subroutine, the subroutine is said to be tail recursive, which is a special case of direct recursion (computer s ...
. Two notable features of Flix are its type and effect system and its support for first-class Datalog constraints. The Flix type and effect system supports Hindley-Milner-style
type inference Type inference refers to the automatic detection of the type of an expression in a formal language. These include programming languages and mathematical type systems, but also natural languages in some branches of computer science and linguistics ...
. The system separates pure and impure code: if an expression is typed as pure then it cannot produce an effect at run-time. Higher-order functions can enforce that they are given pure (or impure) function arguments. The type and effect system supports effect polymorphism which means that the effect of a higher-order function may depend on the effect(s) of its argument(s). Flix supports
Datalog Datalog is a declarative logic programming language. While it is syntactically a subset of Prolog, Datalog generally uses a bottom-up rather than top-down evaluation model. This difference yields significantly different behavior and properties ...
programs as first-class values. A Datalog program value, i.e. a collection of Datalog facts and rules, can be passed to and returned from functions, stored in data structures, and composed with other Datalog program values. The minimal model of a Datalog program value can be computed and is itself a Datalog program value. In this way, Flix can be viewed as a meta programming language for Datalog. Flix supports stratified negation and the Flix compiler ensures stratification at compile-time. Flix also supports an enriched form of Datalog constraints where predicates are given
lattice Lattice may refer to: Arts and design * Latticework, an ornamental criss-crossed framework, an arrangement of crossing laths or other thin strips of material * Lattice (music), an organized grid model of pitch ratios * Lattice (pastry), an ornam ...
semantics.


Overview

Flix is a
programming language A programming language is a system of notation for writing computer programs. Most programming languages are text-based formal languages, but they may also be graphical. They are a kind of computer language. The description of a programming ...
in the ML-family of languages. Its type and effect system is based on Hindley-Milner with several extensions, including
row polymorphism In programming language type theory In mathematics, logic, and computer science, a type theory is the formal presentation of a specific type system, and in general type theory is the academic study of type systems. Some type theories serve a ...
and Boolean unification. The syntax of Flix is inspired by Scala and uses short keywords and
curly braces A bracket is either of two tall fore- or back-facing punctuation marks commonly used to isolate a segment of text or data from its surroundings. Typically deployed in symmetric pairs, an individual bracket may be identified as a 'left' or 'r ...
. Flix supports
uniform function call syntax Uniform Function Call Syntax (UFCS) or Uniform Calling Syntax (UCS) or sometimes Universal Function Call Syntax is a programming language feature in D and Nim that allows any function to be called using the syntax for method calls (as in object-o ...
which allows a function call f(x, y, z) to be written as x.f(y, z). The concurrency model of Flix is inspired by Go and based on channels and processes. A process is a light-weight thread that does not share (mutable) memory with another process. Processes communicate over channels which are bounded or unbounded queues of immutable messages. While many programming languages support a mixture of functional and imperative programming, the Flix type and effect system tracks the purity of every expression making it possible to write parts of a Flix program in a purely functional style with purity enforced by the effect system. Flix programs compile to JVM bytecode and are executable on the
Java Virtual Machine A Java virtual machine (JVM) is a virtual machine that enables a computer to run Java programs as well as programs written in other languages that are also compiled to Java bytecode. The JVM is detailed by a specification that formally describes ...
(JVM). The Flix compiler performs whole program compilation, eliminates polymorphism via
monomorphization In programming languages, monomorphization is a compile-time process where polymorphic functions are replaced by many monomorphic functions for each unique instantiation. This transformation is desirable, since then the output intermediate represe ...
, and uses tree shaking to remove
unreachable code In computer programming, unreachable code is part of the source code of a program which can never be executed because there exists no control flow path to the code from the rest of the program. Unreachable code is sometimes also called ''dead code' ...
. Monomorphization avoids
boxing Boxing (also known as "Western boxing" or "pugilism") is a combat sport in which two people, usually wearing protective gloves and other protective equipment such as hand wraps and mouthguards, throw punches at each other for a predetermined ...
of primitive values at the cost of longer compilation times and larger executable binaries. Flix has some support for interoperability with programs written in
Java Java (; id, Jawa, ; jv, ꦗꦮ; su, ) is one of the Greater Sunda Islands in Indonesia. It is bordered by the Indian Ocean to the south and the Java Sea to the north. With a population of 151.6 million people, Java is the world's List ...
. Flix supports
tail call elimination In computer science, a tail call is a subroutine call performed as the final action of a procedure. If the target of a tail is the same subroutine, the subroutine is said to be tail recursive, which is a special case of direct recursion (computer s ...
which ensures that function calls in tail position never consume stack space and hence cannot cause the call stack to overflow. Since the JVM instruction set lacks explicit support for tail calls, such calls are emulated using a form of reusable stack frames. Support for tail call elimination is important since all iteration in Flix is expressed through
recursion Recursion (adjective: ''recursive'') occurs when a thing is defined in terms of itself or of its type. Recursion is used in a variety of disciplines ranging from linguistics to logic. The most common application of recursion is in mathematics ...
. The Flix compiler disallows most forms of unused or redundant code, including: unused local variables, unused functions, unused formal parameters, unused type parameters, and unused type declarations, such unused constructs are reported as compiler errors.
Variable shadowing In computer programming, variable shadowing occurs when a variable declared within a certain scope (decision block, method, or inner class) has the same name as a variable declared in an outer scope. At the level of identifiers (names, rather than ...
is also disallowed. The stated rationale is that unused or redundant code is often correlated with erroneous code A
Visual Studio Code Visual Studio Code, also commonly referred to as VS Code, is a source-code editor made by Microsoft with the Electron Framework, for Windows, Linux and macOS. Features include support for debugging, syntax highlighting, intelligent code complet ...
extension for Flix is available. The extension is based on the
Language Server Protocol The Language Server Protocol (LSP) is an open, JSON-RPC-based protocol for use between source code editors or integrated development environments (IDEs) and servers that provide programming language-specific features like code completion, syntax hig ...
, a common interface between
IDEs Ides or IDES may refer to: Calendar dates * Ides (calendar), a day in the Roman calendar that fell roughly in the middle of the month. In March, May, July, and October it was the 15th day of the month; in other months it was the 13th. **Ides of Mar ...
and
compilers In computing, a compiler is a computer program that translates computer code written in one programming language (the ''source'' language) into another language (the ''target'' language). The name "compiler" is primarily used for programs that ...
being developed by
Microsoft Microsoft Corporation is an American multinational technology corporation producing computer software, consumer electronics, personal computers, and related services headquartered at the Microsoft Redmond campus located in Redmond, Washing ...
. Flix is
open source software Open-source software (OSS) is computer software that is released under a license in which the copyright holder grants users the rights to use, study, change, and distribute the software and its source code to anyone and for any purpose. Open ...
available under the Apache 2.0 License.


Examples


Hello world

The following program prints " Hello World!" when compiled and executed: def main(): Unit & Impure = Console.printLine("Hello World!") The type and effect signature of the main function specifies that it has no parameters, returns a value of type Unit, and that the function is impure. The main function is impure because it invokes printLine which is impure.


Algebraic data types and pattern matching

The following program fragment declares an
algebraic data type In computer programming, especially functional programming and type theory, an algebraic data type (ADT) is a kind of composite type, i.e., a type formed by combining other types. Two common classes of algebraic types are product types (i.e., t ...
(ADT) named Shape: enum Shape The ADT has three constructors: Circle, Square, and Rectangle. The following program fragment uses
pattern matching In computer science, pattern matching is the act of checking a given sequence of tokens for the presence of the constituents of some pattern. In contrast to pattern recognition, the match usually has to be exact: "either it will or will not be ...
to destruct a Shape value: def area(s: Shape): Int = match s


Higher-order functions

The following program fragment defines a
higher-order function In mathematics and computer science, a higher-order function (HOF) is a function that does at least one of the following: * takes one or more functions as arguments (i.e. a procedural parameter, which is a parameter of a procedure that is itself ...
named twice that when given a function f from Int to Int returns a function that applies f to its input two times: def twice(f: Int -> Int): Int -> Int = x -> f(f(x)) We can use the function twice as follows: twice(x -> x + 1)(0) Here the call to twice(x -> x + 1) returns a function that will increment its argument two times. Thus the result of the whole expression is 0 + 1 + 1 = 2.


Parametric polymorphism

The following program fragment illustrates a
polymorphic function In programming language theory and type theory, polymorphism is the provision of a single interface (computing), interface to entities of different Data type, types or the use of a single symbol to represent multiple different types.: "Polymorph ...
that maps a function f: a -> b over a list of elements of type a returning a list of elements of type b: def map(f: a -> b, l: List : List = match l The map function recursively traverses the list l and applies f to each element constructing a new list. Flix supports type parameter elision hence it is not required that the type parameters a and b are explicitly introduced.


Extensible records

The following program fragment shows how to construct a record with two fields x and y: def point2d(): = Flix uses
row polymorphism In programming language type theory In mathematics, logic, and computer science, a type theory is the formal presentation of a specific type system, and in general type theory is the academic study of type systems. Some type theories serve a ...
to type records. The sum function below takes a record that has x and y fields (and possibly other fields) and returns the sum of the two fields: def sum(r: ): Int = r.x + r.y The following are all valid calls to the sum function: sum() sum() sum()


Notable features


Polymorphic effects

The Flix type and effect system separates pure and impure expressions. A pure expression is guaranteed to be
referentially transparent In computer science, referential transparency and referential opacity are properties of parts of computer programs. An expression is called ''referentially transparent'' if it can be replaced with its corresponding value (and vice-versa) withou ...
. A pure function always returns the same value when given the same argument(s) and cannot have any (observable) side-effects. For example, the following expression is of type Int and is Pure: 1 + 2 : Int & Pure whereas the following expression is Impure: Console.printLine("Hello World") : Unit & Impure A higher-order function can specify that a function argument must be pure, impure, or that it is effect polymorphic. For example, the definition of Set.exists requires that its function argument f is pure: // The syntax a -> Bool is short-hand for a -> Bool & Pure def exists(f: a -> Bool, xs: Set : Bool = ... The requirement that f must be pure ensures that implementation details do not
leak A leak is a way (usually an opening) for fluid to escape a container or fluid-containing system, such as a tank or a ship's hull, through which the contents of the container can escape or outside matter can enter the container. Leaks are usuall ...
. For example, since f is pure it cannot be used to determine in what order the elements of the set are traversed. If f was impure such details could leak, e.g. by passing a function that also prints the current element, revealing the internal element order inside the set. A higher-order function can also require that a function is impure. For example, the definition of List.foreach requires that its function argument f is impure: // The syntax a ~> Unit is short-hand for a -> Unit & Impure def foreach(f: a ~> Unit, xs: List : Unit & Impure The requirement that f must be impure ensures that the code makes sense: It would be meaningless to call List.foreach with a pure function since it always returns Unit. The type and effect is sound, but not complete. That is, if a function is pure then it ''cannot'' cause an effect, whereas if a function is impure then it ''may'', but not necessarily, cause an effect. For example, the following expression is impure even though it cannot produce an effect at run-time: if (1

2) Console.printLine("Hello World!") else ()
A higher-order function can also be effect polymorphic: its effect(s) can depend on its argument(s). For example, the standard library definition of List.map is effect polymorphic: def map(f: a -> b & e, xs: List : List & e The List.map function takes a function f from elements of type a to b with effect e. The effect of the map function is itself e. Consequently, if List.map is invoked with a pure function then the entire expression is pure whereas if it is invoked with an impure function then the entire expression is impure. It is effect polymorphic. A higher-order function that takes multiple function arguments may combine their effects. For example, the standard library definition of forward
function composition In mathematics, function composition is an operation that takes two functions and , and produces a function such that . In this operation, the function is applied to the result of applying the function to . That is, the functions and ...
>> is pure if both its function arguments are pure: def >>(f: a -> b & e1, g: b -> c & e2): a -> c & (e1 and e2) = x -> g(f(x)) The type and effect signature can be understood as follows: The >> function takes two function arguments: f with effect e1 and g with effect e2. The effect of >> is effect polymorphic in the
conjunction Conjunction may refer to: * Conjunction (grammar), a part of speech * Logical conjunction, a mathematical operator ** Conjunction introduction, a rule of inference of propositional logic * Conjunction (astronomy), in which two astronomical bodies ...
of e1 and e2. If both are pure (their effect is true) then the overall expression is pure (true). Otherwise it is impure. The type and effect system allows arbitrary boolean expressions to control the purity of function arguments. For example, it is possible to express a higher-order function h that accepts two function arguments f and g of which at most one is impure: def h(f: a -> b & e1, g: b -> c & (not e1 or e2)): Unit If h is called with a function argument f which is impure (false) then the second argument must be pure (true). Conversely, if f is pure (true) then g may be pure (true) or impure (false). It is a compile-time error to call h with two impure functions. The type and effect system can be used to ensure that statement expressions are useful, i.e. that if an expression or function is evaluated and its result is discarded then it must have a side-effect. For example, compiling the program fragment below: def main(): Unit & Impure = List.map(x -> 2 * x, 1 :: 2 :: Nil); Console.printLine("Hello World") causes a compiler error: -- Redundancy Error -------------------------------------------------- >> Useless expression: It has no side-effect(s) and its result is discarded. 2 , List.map(x -> 2 * x, 1 :: 2 :: Nil); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ useless expression. because it is non-sensical to evaluate the pure expression List.map(x -> 2 * x, 1 :: 2 :: Nil) and then to discard its result. Most likely the programmer wanted to use the result (or alternatively the expression is redundant and could be deleted). Consequently, Flix rejects such programs.


First-class datalog constraints

Flix supports
Datalog Datalog is a declarative logic programming language. While it is syntactically a subset of Prolog, Datalog generally uses a bottom-up rather than top-down evaluation model. This difference yields significantly different behavior and properties ...
programs as first-class values. A Datalog program is a logic program that consists of a collection of unordered
fact A fact is a datum about one or more aspects of a circumstance, which, if accepted as true and proven true, allows a logical conclusion to be reached on a true–false evaluation. Standard reference works are often used to check facts. Scient ...
s and
rules Rule or ruling may refer to: Education * Royal University of Law and Economics (RULE), a university in Cambodia Human activity * The exercise of political or personal control by someone with authority or power * Business rule, a rule perta ...
. Together, the facts and rules imply a minimal model, a unique solution to any Datalog program. In Flix, Datalog program values can be passed to and returned from functions, stored in data structures, composed with other Datalog program values, and solved. The solution to a Datalog program (the minimal model) is itself a Datalog program. Thus, it is possible to construct pipelines of Datalog programs where the solution, i.e. "output", of one Datalog program becomes the "input" to another Datalog program. The following edge facts define a graph: Edge(1, 2). Edge(2, 3). Edge(3, 4). The following Datalog rules compute the
transitive closure In mathematics, the transitive closure of a binary relation on a set is the smallest relation on that contains and is transitive. For finite sets, "smallest" can be taken in its usual sense, of having the fewest related pairs; for infinite s ...
of the edge relation: Path(x, y) :- Edge(x, y). Path(x, z) :- Path(x, y), Edge(y, z). The minimal model of the facts and rules is: Edge(1, 2). Edge(2, 3). Edge(3, 4). Path(1, 2). Path(2, 3). Path(3, 4). Path(1, 3). Path(1, 4). Path(2, 4). In Flix, Datalog programs are values. The above program can be embedded in Flix as follows: def main(): # = let f = #; let p = #; solve f <+> p The local variable f holds a Datalog program value that consists of the edge facts. Similarly, the local variable p is a Datalog program value that consists of the two rules. The f <+> p expression computes the composition (i.e. union) of the two Datalog programs f and p. The solve expression computes the minimal model of the combined Datalog program, returning the edge and path facts shown above. Since Datalog programs are first-class values, we can refactor the above program into several functions. For example: def edges(): # = # def closure(): # = # def main(): # = solve edges() <+> closure() The un-directed closure of the graph can be computed by adding the rule: Path(x, y) :- Path(y, x). We can modify the closure function to take a Boolean argument that determines whether we want to compute the directed or un-directed closure: def closure(directed: Bool): # = let p1 = #; let p2 = #; if (directed) p1 else (p1 <+> p2)


Type-safe composition

The Flix type system ensures that Datalog program values are well-typed. For example, the following program fragment does not type check: let p1 = Edge(123, 456).; let p2 = Edge("a", "b").; p1 <+> p2; because in p1 the type of the Edge predicate is Edge(Int, Int) whereas in p2 it has type Edge(String, String). The Flix compiler rejects such programs as ill-typed.


Stratified negation

The Flix compiler ensures that every Datalog program value constructed at run-time is
stratified Stratification may refer to: Mathematics * Stratification (mathematics), any consistent assignment of numbers to predicate symbols * Data stratification in statistics Earth sciences * Stable and unstable stratification * Stratification, or st ...
. Stratification is important because it guarantees the existence of a unique minimal model in the presence of negation. Intuitively, a Datalog program is stratified if there is no recursion through negation, i.e. a predicate cannot depend negatively on itself. Given a Datalog program, a
cycle detection In computer science, cycle detection or cycle finding is the algorithmic problem of finding a cycle in a sequence of iterated function values. For any function that maps a finite set to itself, and any initial value in , the sequence of itera ...
algorithm can be used to determine if it is stratified. For example, the following Flix program contains an expression that cannot be stratified: def main(): # = let p1 = Husband(x) :- Male(x), not Bachelor(x).; let p2 = Bachelor(x) :- Male(x), not Husband(x).; p1 <+> p2 // illegal, not stratified. because the last expression constructs a Datalog program value whose
precedence graph A precedence graph, also named conflict graph and serializability graph, is used in the context of concurrency control in databases. The precedence graph for a schedule S contains: * A node for each committed transaction in S * An arc from Ti to Tj ...
contains a negative cycle: the Bachelor predicate negatively depends on the Husband predicate which in turn (positively) depends on the Bachelor predicate. The Flix compiler computes the precedence graph for every Datalog program valued expression and determines its stratification at compile-time. If an expression is not stratified, the program is rejected by the compiler. The stratification is sound, but conservative. For example, the following program is unfairly rejected: def main(): # = if (true) A(x) :- A(x), not B(x). else B(x) :- B(x), not A(x). The type system conservatively assumes that both branches of the if expression can be taken and consequently infers that there may be a negative cycle between the A and B predicates. Thus the program is rejected. This is despite the fact that at run-time the main function always returns a stratified Datalog program value.


Design philosophy

Flix is designed around a collection of stated principles: * Everything is an expression. Most Flix constructs, except for top-level declarations, are expressions that evaluate to values. *
Closed-world assumption The closed-world assumption (CWA), in a formal system of logic used for knowledge representation, is the presumption that a statement that is true is also known to be true. Therefore, conversely, what is not currently known to be true, is false. Th ...
. The Flix compiler assumes that the source code of the entire program is available at compile-time. * Pure and impure code is separated. The type and effect system precisely captures whether an expression may produce an effect * The language has no compile-time warnings, only errors. The principles also list several programming language features that have been deliberately omitted. In particular, Flix lacks support for: * Null values. Instead the use of the Option data type is recommended. * Implicit coercions. Instead type conversions must be performed explicitly by the programmer. *
Reflection Reflection or reflexion may refer to: Science and technology * Reflection (physics), a common wave phenomenon ** Specular reflection, reflection from a smooth surface *** Mirror image, a reflection in a mirror or in water ** Signal reflection, in ...
. The programmer cannot reflect over the structure of the program at run-time.


References

{{reflist


External links


Official website

Flix implementation source code
hosted at
GitHub GitHub, Inc. () is an Internet hosting service for software development and version control using Git. It provides the distributed version control of Git plus access control, bug tracking, software feature requests, task management, continuous ...
. Procedural programming languages Functional languages Logic programming languages High-level programming languages