> you mostly model data with sum types, which in my mind are the best way to model data
>it's very similar to the language I wanted to build (in particular, we could reuse built-in immutable data structures for Dark's values)
> it had a reputation for being high-performance, which meant that we could write an interpreter for Dark and not have it be terribly slow (vs writing an interpreter in python, which might be too slow)
> We have struggled to make editor tooling work for us.
> Lack of libraries
> Multicore is coming Any Day Now™, and while this wasn't a huge deal for us, it was annoying.
> we also use ReasonML
> lazyness and pure functional-ness I felt were not great
> I plan to leave keep the frontend in ReasonML
I don't want to sound too much like a fanboy, but all this really sounds like Scala. You want Scala.
Compare https://rescript-lang.org/docs/manual/latest/shared-data-typ... with how Scala.js represents arrays and records.
Regarding interop and the link you posted, here is the equivalent documentation page in Scala.js: https://www.scala-js.org/doc/interoperability/types.html
ReScript is more correct than scala, ReScript's type system(essentially borrowed from OCaml) is sound while scala is unsound.
> Using `undefined` to represent `None` means that you cannot tell the difference between `None` and `Some(None)`
I don't know where you get the impression, of course they are different in ReScript.
One thing missed is that ReScript compiler may be 10-100 times faster than Scala.js (I am not kidding)
Ah, I did not make it clear what I meant by "correct" in this context. I meant "faithful to the original language" (OCaml natively compiled and Scala on the JVM).
You are of course right about the type systems. (I believe many soundness holes are fixed in Scala 3.)
> > Using `undefined` to represent `None` means that you cannot tell the difference between `None` and `Some(None)`
> I don't know where you get the impression, of course they are different in ReScript.
I get that impression from the document linked above, which says that `None` is represented as `undefined` and `Some(x)` is represented as `x`, so `Some(None)` must be represented as `undefined` as well. Oh and `()` too. Do at run-time you can't tell which is which.
> One thing missed is that ReScript compiler may be 100 times faster than Scala.js (I am not kidding)
Yes, I believe that's true. It is often mentioned as the biggest weakness is Scala, by advocates and detractors alike.
There are use cases where OCaml’s type system leads to elegant type-safe code that, if expressed in F#, involve inelegant unsafe boilerplate. It’s usually not a whole lot and it’s typically routine but it certainly happens. For some teams and products OCaml is a better choice.
But there’s also a reason why many developers abandoned Lisp for Python: some theoretical fanciness and robustness only infrequently outweighs ease-of-use and quality-of-life.
I think you mean borrow checker for ownership issues...
> why doesn't Haskell take the same route as Rust and ditch its garbage collector?
The Rust language was designed in a way (with ownership) that doesn't need a GC, while Haskell have many different properties (such as lazy evaluate everything) that I don't think would work well without a GC.
The borrow checker is usually a nuisance when you're writing highly mutable code, which isn't a problem for functional code. If you need to store the same data in multiple structures, the sibling is right, .clone() and .to_owned() will usually fix your problem with a very small overhead.
Most of the other cases, you can use typed_arena or bumpalo.
The other 0.1% of the time, you'd have to be pedantic about allocation anyway and might appreciate the compiler's help.
You the author or something? I'm actually learning F# and OCaml and am interested in Scala as well.
 just looked at it again and while it doesn't default to mutability, it doesn't default to immutability either (i.e. to compare it with F#, it's 'val vs var' versus 'let vs let mutable', so the latter being much longer means the default/lazy thing to do by devs is immutability, in the F# case).
val immutableValue = mutable.Map.empty
let clearlyImmutable: Map<String, Int> = Map::new()
clearlyImmutable.insert("hello", 10) // Does not compile
let mut clearlyMutable: Map<String, Int> = Map::new()
clearlyMutable.insert("hello", 10) // Compiles :)
However, it would be disingenuous to not mention that you can run very quickly in the wild into scala code that is just "java without semicolons". You can very deliberately break null safety in scala. For example `val x: (Int, Double) = null` does compile. You won't normally run into `null` unless you are interacting with java libs or with code from programmers who don't understand type safety.
I want to point out: scala let you do all those nasty things, but this will be a deliberate choice. The mutable data structure from the standard library are clearly marked. There is no methods in the standard library that returns null (outside of regex methods which are thin wrappers around java regexes). Writing mutable code requires a completely different architecture and design choices. If you are in an immutable team, it will be very hard to justify that kind of code. While in a mutable team, the reverse might be true.
In Rust the 'interor mutability' term is used for types like `Mutex`/`RefCell`, `Cell`, or atomics, which are mutable even through shared references. These come with the same problems as interor mutability in scala.