> Separate pure and impure code
So IIUC, the effect system [2] means impurity is contagious. This seems reasonable to me, though I wonder what that means in practice. `Console.printLine` is impure [3], and presumably any debug logging would be too; the contagion might spread pretty far. I wonder if this is why seemingly pure functions like `Array.find` are flagged as impure [4].
> Principle of least surprise: ... and when there is no immediately obvious default, we should not have a default at all, but force the programmer to be explicit about his or her intention.
I like that they called this part out. I'd wager most code really doesn't have sane defaults, just the defaults that made sense to the developer at the time.
> Local type inference
I find that in languages with type signatures (without inference), folks lean on the type to convey information, and in dynamic languages like Clojure, folks lean on good naming. Based on my not-much experience with type inference, the code ends up with neither. Sure the compiler can figure out what it is, but the humans not so much.
> Uniform function call syntax: ... the function call length(xs) can also be written as xs.length()
I wonder what their motivation was for this; my guess is to appeal to both FP and OO devs. IMO, having multiple ways of writing the same thing is likely to lead to unnecessary style wars.
> Private by default: ... declarations are hidden by default (i.e. private) and cannot be accessed from outside of their namespace
I like this a lot, though I foresee running into issues with unit testing, and thereby arguments over at what level things should be tested.
> Bugs are not recoverable errors: ... For recoverable errors, we should enforce that they are checked and handled. For program bugs, we should terminate execution as quickly as possible
This seems reasonable to me, but I'm curious what counts as a "program bug", or how to know that some error is recoverable.
> No null value
Meh. I've think Option is just null with extra steps. NPEs arise because of method invocation, e.g., `x.foo()` while `x` is null. In FP languages like Clojure, NPEs don't really happen since `x` is just a value being passed to a function. With nil-punning and reasonable behavior of core functions (e.g., `(get m k)` yields nil when `m` is nil), an unexpected nil is really a type error.
> No dead or unreachable code / No unused variables
Perhaps it's from my REPL-driven, exploratory style of development, but I think having to constantly comment out code I'm ambivalent about just to get it to compile would be a bit annoying. I'd think something like this should be a compile option, but I think that runs afoul of their "One Language"/"no flags" principle.
> No variadic (varargs) functions
Having worked with enough Clojure, and seen enough bugs when using `apply` on vararg fns, I think this is a good choice. IIUC, their approach would replace, e.g., `(+ x y z ...)` with `(sum [x y z ...])`.
> No binary or octal literals: ... It is our understanding that these features are rarely used in practice.
Until you need to do some bit-twiddling; maybe they still have hex. I wonder what they considered the downside of including it.
[1] https://flix.dev/principles/
[2] https://flix.dev/innovations/