Flix – Safe, reliable, concise, and functional-first programming language
flix.dev
flix.dev
def reachable(g: List[(String, Int32, String)], minSpeed: Int32): List[(String, String)] =
let facts = project g into Road;
let rules = #{
Path(x, y) :- Road(x, maxSpeed, y), if maxSpeed >= minSpeed.
Path(x, z) :- Path(x, y), Road(y, maxSpeed, z), if maxSpeed >= minSpeed.
};
query facts, rules select (src, dst) from Path(src, dst) |> Foldable.toList
Composing datalog natively like any other type – very cool.I'm naive. What does this mean and why is it important?
(I am one of the authors of Flix.)
// Given an edge x -> y, there is a path x -> y.
path(x, y) :- edge(x, y).
// Given an edge x -> z and a path z -> y, there is a path x -> y.
path(x, y) :- edge(x, y: z), path(x: z, y).
Here is the equivalent recursive SQL (SQLite syntax): WITH RECURSIVE path(x, y) AS (
SELECT
edge.x AS x,
edge.y AS y
FROM
edge
UNION
SELECT
edge.x AS x,
path.y AS y
FROM
edge,
path
WHERE
edge.y = path.x
)
SELECT
DISTINCT *
FROM
path
If you want to play with Datalog, https://percival.ink is an online notebook with a very gentle introduction. I also published a version of percival.ink that converts simple Datalog queries to recursive SQLite queries here: https://percival.jake.tl (the example above is from percival.jake.tl).As jorkadeen said in their answer (I added emphasis to the last sentence):
> Datalog programs are values. You can store them in local variables, pass them as arguments, and return them. If you have two Datalog values you can combine them into one (effectively it's just the union of them). This allows you to write small "Datalog fragments" and stitch them together into a larger program. The type system ensures that this stays meaningful.
Why this is important: From my perspective is that a very large percent of industrial code interacts with data (in databases) by building query strings. In most languages, we throw away all the nice features of the language when it comes to data access. To regain some level of composability and safety, developers pour years into libraries like [Arel in Ruby](https://www.rubydoc.info/gems/arel) or [Calcite in Java](https://calcite.apache.org/docs/algebra.html). The most notable example of language native database access is [Language Integrated Query (LINQ)](https://docs.microsoft.com/en-us/dotnet/csharp/programming-g...) in C#. But all of these tools are hampered by the constraints of SQL. SQL is very good for flat, normalized data with one or two joins. Once you begin working with trees or graphs, tools based around SQL become cumbersome.
Using datalog instead of SQL means you have a logical langauge that is inherently more accepting of composition in a theoretical way. But, most systems that speak datalog today are extremely specialized. [Souffle](https://souffle-lang.github.io/simple) and [differential-datalog](https://github.com/vmware/differential-datalog) are useful ahead-of-time compilers for datalog, but you have to design your whole program around using them. The datalog variant [inside Datomic](https://docs.datomic.com/on-prem/query/query.html) is dynamic, but is only usable for accessing data in a data store. Good luck using it for analysis of data in memory.
Including datalog into the language as first-class values mean that you can access the power of datalog and actually compose it in practice.
[Note: I've never used Flix – just a datalog fan.]
So to me it seems a little strange the definition of path in datalog from a prolog user point of view.
Good thing of having common AST in language is libs interoperability.
It’s like normal lanugages had SQL support built-in (except you can’t do that easily as SQL is order dependent)
In Defense of Programming Languages - https://news.ycombinator.com/item?id=27799063 - July 2021 (1 comment)
Flix – Next-generation reliable, concise, functional-first programming language - https://news.ycombinator.com/item?id=25513397 - Dec 2020 (84 comments)
The Flix Programming Language - https://news.ycombinator.com/item?id=19928153 - May 2019 (2 comments)
Is that accurate?I was hoping for something more like Koka.
Otherwise that is a very intriguing combination of features, especially first class Datalog constraints.
Any plans for additional backends? (native, Webassembly)?
def swap!(a: Array[a, r], i: Int32, j: Int32): Unit \ { Read(r), Write(r) }
We are aware of algebraic effects and handlers, but let's just say that we prefer to underpromise and overdeliver.(I am one of the authors of Flix.)
- is there any plans to add restricted forms of instance overlapping, in the spirit of purescript's instance chains (or Haskell's overlappable/overlapping)?
- I couldn't find resources on error handling, does flix use some sort of Either/Result type? and if so, how ergonomic is it? I'm assuming Flix doesn't have special syntax like Scala/Haskell for monadic for(/do) comprehensions, so I fear that Either based error handling would quickly become gnarly with just monadic operators. Is there any example of Result-dense code out there to check out? I couldn't find it in the example (admittedly, I have checked quite thoroughly yet)
- I read somewhere on the docs (I think on the design flaws blog?) that Flix at one point had UFCS, is that still supported? I can't seem to make it work on the playground
- Is there anywhere I can read up on the way instance resolution works? More specifically, I'm interested in whether Flix takes only instance heads or it also takes the constraints into account when choosing an instance (if it does take constraints into account, that makes overlapping instances a lot less useful in a good way, I think?)
In any case, great work on the language! I will for sure try this out the first time I get the chance, it looks great and has basically 99% of the features that I would ever want in a programming language. Keep up the good work!
- We are debating whether to allow a limited form of overlapping instances. No decision has been made yet.
- Flix uses the Options and Errors. Flix also has monadic let* making it easier to work with these two types.
- UFCS had to be removed because it caused unavoidable ambiguities with records :(
- I am not sure I understand the question about instance resolution. Feel free to stop by our Gitter channel to discuss further.
Ref. _What color is your function_
What is the overall status of the implementation, particularly for real world applications (features like talking to a network, file systems, databases and operating system, and/or FFIs for JVM or non-JVM languages)?
> Flix borrows a lot of syntax from Scala, hence the two languages have a similar feel. We think Scala made many good design choices with respect to syntax, including: (a) the use of short keywords, (b) the x : T syntax for type annotations, (c) the List[Int32] syntax for type parameters, and (d) if, match, etc. as expressions.
> Other than syntax, the two languages are very different: Scala is object-oriented, Flix is not. Scala has sub-typing, Flix does not. The Scala type system is unsound and has imperfect type inference, whereas the Flix type system is both sound and supports type inference.
"Ensure that you have at least Java 11 installed. "
How about a compiler or an interpreter ?
"Download the latest version of the Flix compiler (flix.jar) at https://github.com/flix/flix/releases/latest.
Create an empty folder (e.g. mkdir flixproject) and place the downloaded Flix JAR (flix.jar) into that folder.
Enter the created directory (e.g. cd flixproject) and run java -jar flix.jar init to create an empty Flix project.
Run java -jar flix.jar run to compile and run the project.
Flix will compile the project and execute the main function (located in src/Main.flix).
"
So getting started is about running an empty project file ? How about some code examples ? I wanted to see what is a "Safe, Reliable, Concise, and Functional-First Programming Language". And all i get is an empty project ?
After reading some docs it looks like a lisp without parantheses and one has to have a good understanding of lambda functions to be able to read the documentation.
I appreciate languages/toolkits that are concise in their tooling. Golang is another language like this. The tooling is simple (though Golang is more opinionated than Nim in things like testing and project layouts) and easy to reason about.
(I am one of the authors of Flix.)
Plus I really like the code example right there on the front page.
> case Rectangle(Int32, Int32)
> def origin(): (Int32, Int32) =
> def twoByFour(): {w :: Int32, h :: Int32} =
case Rectangle: (Int32, Int32)
def origin(): (Int32, Int32)
def twoByFour(): (w: Int32, h: Int32)
?
Parens vs curly braces. Single : vs double ::.
Yes, of different things that look different, then asked why.