Hacker Newsnew | past | comments | ask | show | jobs | submit | jorkadeen's commentslogin

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 :-)


Flix looks great! I was speaking to the unison quirks.


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.


We plan to explore semicolon inference in the future; but there are a lot of dangerous corner cases to consider.


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.


But lists and trees aren't really "built into" languages. They're part of the standard library, not the language itself. Maybe one could say "foreach" syntax builds lists into a language, but in many languages you can foreach non-lists, others have foreach as a method instead of syntax, and it still doesn't say anything about trees.

I do see your point, I'm just not so sure I think it'd be the right thing to do. It feels like too big and arbitrary to be a core language feature, better left to a library. If there are some core features that would make building such a library easier, I'd focus on those rather than the logic programming itself. Something like how Rust did async. (Though contrarily, I think Rust should have built async into the language, since it's pervasive and hard to interop different implementations. Unlike async, logic programming is typically self-contained, and there would rarely be a need to interop multiple implementations).

Anyway, great work so far. I look forward to seeing it progress.


Going a little further, what I'd really like to see is the core concepts implemented in a way that allows a standard implementation to follow straightforwardly from the type system, but also allow for doing nonstandard things. Like, "here's a logic language built in" is kind of boring. What's more enticing is "Here are the components. Here's a nominal implementation based on these components. Here's a weird thing you can do that we hadn't actually designed for, but could fit certain use cases."

I remember Jon Skeet did a whole series of blog posts on reimplementing C# async, which was possible because C#'s async syntax isn't bolted to the implementation, and added features like coroutines that weren't part of C# at all. https://codeblog.jonskeet.uk/2011/06/22/eduasync-part-13-fir.... Similarly Daniel Earwicker had a series where he implemented async/await using iterator syntax instead https://smellegantcode.wordpress.com/2010/12/14/unification-....

And sure, now I know it's all kind of fancy but fairly trivial stuff you can do with monads (and I guess free monads in the Linq-to-SQL case), but it was fascinating to me at the time.

So yeah, for "selling" purposes, I think rather than selling datalog built into the language as a front-page feature, a series of "how to build a datalog" posts would go further in showing off the power of the components of the language that it's built from.

(And FWIW I do like the way C# has built-in support for "important" monads like iterators (foreach), generators (yield), async (await), optional (null propagation operators), etc., even though a language purist would argue against it. I think it provides an easier on-ramp for newer developers, and helps make common things more concise and readable. So it'd be interesting to see where that line would best get drawn for logic programming, what gets special-but-extensible syntax support, and what is purely implementation and functions).


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.


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.


But you have user defined effects don't you? E.g say I define an effect ReadsFromDB, it doesn't necessarily do what it says on the tin, and there is no way a compiler can check that it does. It could read from the db, and send some rockets into space. So a consequence of that is that these "effect systems" just amount to giving names to blocks of code. That's not necessarily a bad thing.


If you define a variable called number_of_apples, there is no way for the compiler to check that it actually contains the number of apples. How is that different?


It's different. Effect systems claim to be a 'major evolution' that 'enforce modularity'. But they don't really enforce anything other than the modularity provided by standard oop classes or if in a fp language, function modules.


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


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.


Sorry, I wasn't clear. Yes, you can have pipelines in Flix, F#, OCaml, to me however placing the "subject" first feels more natural, as function signatures (not necessarily in pipelines) have a symmetry not encountered otherwise:

Subject First:

  Map.put(map, key, value)
  Map.get(map, key)
  Map.get(map, key, default)
  Map.del(map, key)
Subject Last:

  Map.put(key, value, map)
  Map.get(key, map)
  Map.get(key, default, map)
  Map.del(key, map)


There is no significant indentation. What leads you to be believe that?


The Python-esque looking function defs.


looks like def x = expression

no indentation involved. did not dig in though


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.


Plus that you regretted having `+` as a concatenation operator.

  However, I believe an even better design choice would be to forgo string concatenation and instead rely entirely on string interpolation. String interpolation is a much more powerful and elegant solution to the problem of building complex strings.
src: https://flix.dev/blog/design-flaws-in-flix


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: