HackerTrans
TopNewTrendsCommentsPastAskShowJobs

jorkadeen

no profile record

Submissions

Making Flix Compiler Errors Helpful and Delightful

blog.flix.dev
4 points·by jorkadeen·há 5 meses·0 comments

Effect Systems vs. Print Debugging: A Pragmatic Solution

blog.flix.dev
11 points·by jorkadeen·há 10 meses·0 comments

comments

jorkadeen
·há 10 meses·discuss
We can certainly extend it to a language feature-- if there are good use cases.

Do you have some specific special effects in mind?
jorkadeen
·há 10 meses·discuss
It was a simple example; whether a specific optimization applies is very tricky. We have to look at the details. When can Rust move or eliminate a binder? Does Rust support automatic parallelization? What happens if you use unsafe blocks to lie to their type and ownership system? I think many of the same issues will surface.

To be me, the interesting question is: What happens when you lie to the type (and effect or ownership) system?
jorkadeen
·há 10 meses·discuss
We think that functional programmers should be able to write e.g. `List.count(x -> x > 5, l)` (or e.g. use pipelines with |>) and have it run as fast as an ordinary imperative loop with a mutable variable. The Flix compiler gives them that-- but it requires the program to undergo certain transformations that may require expressions to be moved around and eliminated. It is dangerous to perform such optimizations with incorrect assumptions about types or effects. How to support that together with print-debugging is the challenge.

For systems in production, we have the `Logger` effect and associated handlers.
jorkadeen
·há 10 meses·discuss
A few other languages are:

- https://effekt-lang.org/

- https://koka-lang.github.io/

- https://www.unison-lang.org/

- https://antelang.org/
jorkadeen
·há 10 meses·discuss
The challenge is that the compiler uses the type-and-effect system for many of its tasks, including whole-program optimization and code generation.

If printing- or logging statements have no effect, the compiler might reorder them or even remove them.
jorkadeen
·há 12 meses·discuss
Flix does not have significant whitespace. Where did you run into trouble? You are welcome to swing by Gitter if you need help. We are friendly :-)
jorkadeen
·há 12 meses·discuss
You can always add your own `safeDiv` function :-) I believe native compilation is possible via Graal native-image-- but I have not yet tried it.
jorkadeen
·há 12 meses·discuss
We plan to explore semicolon inference in the future; but there are a lot of dangerous corner cases to consider.
jorkadeen
·há 12 meses·discuss
Thanks-- I fixed the links!
jorkadeen
·há 12 meses·discuss
The counter-point is the following: Functional programming is great for working with lists and trees. But functional programming (and imperative programming) struggle with succinctly, correctly, and efficiently expressing queries on graphs. Datalog, on the other hand, is excellent for working with graphs. It is simple, expressive, and (can be) very fast. It is a power tool. Most of the time it should not be used, but when it fits the problem domain its benefit can be 10x or 100x. It is also worth pointing out that Datalog is strictly more powerful than SQL (modulo various extensions).

The goal of Flix -- and typically of any high-level programming language -- is to provide powerful abstractions and constructs that make programming simple, concise, and (often) less error-prone. Here Datalog fits perfectly.

Now that said -- looking through the Flix documentation -- I think we need to do a better job at selling the use case for Datalog. Partly by adding arguments such as the above and partly by adding better examples.
jorkadeen
·há 12 meses·discuss
In the uncommon case, some stack frames must be heap allocated.

This is unavoidable when (a) the runtime enviroment, here the JVM, does not support tail calls, and (b) the language wants to guarantee that _any_[1] tail call does not grow the stack.

[1] Any call. Not just a call to the same function.
jorkadeen
·há 12 meses·discuss
What do you mean? In Flix, if a function has "Bool" as a return type then it can only return a Boolean value. That's what a type system ensures. Similarly, in Flix if a function has the "ReadsFromDB" effect then it can call operations that cause "ReadsFromDB"-- but it cannot cause any other effect. In particular, if there is also a "WriteToDb" then it cannot perform that effect.

This is not just aspirational. It is an iron-clad guarantee; it is what is formally called "effect safety" and it has been proven for calculi that model the Flix type and effect system.

To sum up: In Flix:

- If a function is pure then it cannot perform side-effects.

- If a function has the Console effect then it can only perform operations defined on Console.

- If a function has the Console and Http effect then it can only perform operations defined on Console and Http.

and so on.
jorkadeen
·há 12 meses·discuss
That's me. But I must admit that I prefer Cholula Hot Sauce.

In addition to the imperative `foreach` construct, Flix has two constructs for applicative[1] and monadic[2] comprehensions: `forA` and `forM`. Since applicatives and monads are related, it is useful that their syntax is similar, since it makes it easy to switch between the two. While having camelCase keywords may seem strange, in this case there is a feeling that it works out well. Certainly, `form` or `fora` would be much worse.

Applicative and monadic programming is not a big part of Flix, but it is something we want to support and make ergonomic. Also, these features may have scary names, but the concepts are not too difficult. See [1] and [2] for simple examples of how these features can be used for e.g. error handling.

[1] https://doc.flix.dev/applicative-for-yield.html [2] https://doc.flix.dev/monadic-for-yield.html
jorkadeen
·ano passado·discuss
But in Flix you can write:

  def main(): Unit \ IO = 
      Map.empty() |>
      Map.insert("Hello", "World") |>
      Map.get("Hello") |>
      println
So I am not sure what you mean? In general, if you like pipelines then you want the "subject" (here the map) to be the last argument. That is how it is in Flix.
jorkadeen
·ano passado·discuss
That's right. Locally scoped mutable memory in Flix is very similar to the ST Monad. The two major differences are: (a) Flix is in direct-style vs. monadic style and (b) we use a type and effect system.

Note that there is no requirement that all mutation must occur within a single function. The requirement is that once you leave the lexical scope then all mutable memory associated with that scope become unreachable. Mutation can certainly span over multiple functions.
jorkadeen
·ano passado·discuss
All excellent points.

I think when it comes to programming language design, sometimes I feel that a design has a 90% chance of being good and a 10% chance of being bad. For the logic constraints (right-to-left vs. left-to-right), I think my confidence is only 70% that we got it right.
jorkadeen
·ano passado·discuss
Not sure I agree. A simple example: If your language has null as a subtype of every type then you will have null ptr exceptions everywhere. If your language does not have a null value then you won't. The situation is not as clear cut as you suggest.

Yes, you can write spaghetti code in any language. But a good language design can help (a) reduce errors and (b) nudge the developer towards writing better code.
jorkadeen
·ano passado·discuss
There is no significant indentation. What leads you to be believe that?
jorkadeen
·ano passado·discuss
The StringBuilder example is just that-- an example that many software developers should be familiar with. The deeper idea is that in Flix one can write a pure function that internally use mutation and imperative programming.
jorkadeen
·ano passado·discuss
The parent poster is correct. We do monomorphization, hence Flix types are unboxed. For example, a `List[Int32]` is a list of primitive integers. There is no boxing and no overhead. The upshot is that sometimes we are faster than Java (which has to do boxing). The downside is larger bytecode size-- which is less of a factor these days.

Caveat: Flix sometimes has to box values on the boundary between Flix and Java code -- e.g. when calling a Java library methods that requires a java.lang.Object due to erasure in Java.