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.
Datalog is a logic programming language for relational data. It is much easier to express recursive relationships in Datalog compared to SQL. The most obvious application of Datalog is for working with graphs. The starter example I've usually seen is "graph reachability", which something like "given a list of edges between nodes, tell me if I can reach node X from node Y".
// 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.]
At first sight, in the examples:
inc1(7) pure, inc2(8) impure, what's the meaning of inc2? (some kind of increment?)
Then in the definion of map there is an argument function f, and in the definition there is a new function g that comes from nowhere, so for me at first sight the examples are not insightful.
At the moment the effect system only distinguishes between pure and impure (and effect polymorphic) expressions, but we are working towards a richer system. For example, we will soon be able to express something like:
As someone who loves Haskell a lot and was looking for a functional language on the JVM, the language looks absolutely awesome. If the richer effect system gets implemented this has a shot to becoming a staple of mine! I'm reading the documentation right now, and I had a few questions:
- 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!
Thanks for the response! I will surely hop on the gitter when I have some time. I'm also really happy about the presence of let*. I guess I have something to try this evening after work :)
The color of your function can be "effect polymorphic". In other words, e.g. List.map works with both pure and impure functions. You don't have to write two versions of List.map.
Many languages running on JVM boast of an FFI allowing use of much existing code already written in Java. Is such, or will such be, possible in Flix?
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)?
I found this[1] section in the documentation on interoperability within the JVM. It looks fairly straightforward to work with. I'm not sure about FFI more generally.
> Flix looks quite similar to Scala. How are the two languages related?
> 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.
Wow, this looks pretty exciting. Like @jitl said, the idea of built-in Datalog support sounds really interesting. I don't know how I missed the earlier announcements of this, but now that I found it I'm definitely going to give it a spin.
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.
Scroll down! The homepage shows examples of ADTs, purity tracking (yay!) and pattern matching on purity (oooooh), effect polymorphism (oooooh again), type classes, HKTs, and an embedded Datalog DSL.
I rather liked Nim's one line install of a binary. Starting and running a project is as simple as "nim c -r <file_name>.nim".
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.
The first one is part of the syntax for defining a sum type, the second is a function returning a tuple, and the third is a function returning a record.
Come on. I’m asking why they chose such different syntaxes for type annotations when it would not have been terribly challenging to keep them consistent. I even gave examples.
The first example isn't a type annotation, it's a type definition. The curly braces are consistent with the literal syntax for records. I agree that the use of double colons in record types seems inconsistent. I assume it resolves some ambiguity.
You are spot on. Its because we reserve `:` for type and kind ascriptions. For example, `42 : Int` or `true : Bool`. But we also have `Bool : Type`, i.e. `true : Bool : Type`. While kinds are not that common in every day code, we aim for consistency with ascriptions rather than with records. Hence we had to use a different symbol in record types.