But what does Scala without Java mean?
three models:
So how much coverage do we need?
The Graal branding covers a few different projects:
GraalVM's JIT is state-of-the-art for supporting dynamic and functional languages on the JVM
Graal-native-image (SubstrateVM) provides AOT compilation and a lightweight runtime for JVM bytecode
Both are awesome!
But SubstrateVM does not have GraalVM's JIT
Both work on Java bytecode, not Scala code
SN has run performance comparisons against Graal but not super recently
SN has advantages because it's optimizer has access to Scala code, whereas Graal only gets the JVM output of scalac
SN's optimizer has comparable performance to Graal, while outputting compact binaries
Graal is better at JVM platform coverage and features
SN is stronger at unsafe memory usage and idiomatic C interop
Haoyi's list:
http://www.lihaoyi.com/post/TheDeathofHypeWhatsNextforScala.html
Scala.js is a slam dunk for the browser
Most use cases call for JVM server, scala.js client
Scala.js gets a lot of great capabilities from the browser or from node.js
Eases implementation of Javalib because more is provided by libuv
JS toolchains are convoluted but much less fragmented than native compilation toolchains
Probably a win over SN for everyday CRUD backend apps
Akka and Spark unlikely to have SN support soon
As the community moves toward FP, legacy Java API's are less of a draw.
JVM is a poor fit for:
- CLI's
- embedded
- sidecars
- C interop
These are all traditionally taught in C, and not often directly exposed in higher-level languages.
My hot take - these are fundamental information structures for low-level programming, and we can do a better job than C at using them
val jPtr:Ptr[Int] = stackalloc[Int]
println(s"jPtr has value ${jPtr} and size ${sizeof[Ptr[Int]]} bytes")
val j:Int = !jPtr
println(s"j has value ${j} and size ${sizeof[Int]}")
!jPtr = 5
println(s"jPtr has value ${jPtr} and size ${sizeof[Ptr[Int]]} bytes")
val j2:Int = !jPtr
println(s"j2 has value ${j2} and size ${sizeof[Int]}, j has value ${j}")
val arraySize = 16 * sizeof[Int]
val allocation:Ptr[Byte] = stdlib.malloc(arraySize)
val intArray = allocation.asInstanceOf[Ptr[Int]]
for (i <- 0 to 16) {
intArray(i) = i * 2
}
for (i <- 0 to 16) {
val address = intArray + i
val item = intArray(i)
val check = !(intArray + i) == intArray(i)
println(s"item $i at address ${intArray + i} has value $item, check: $check")
}
// just to be safe
stdlib.free(allocation)
C-level performance for working with contiguous data
Clean model for interfacing with safe code
Syntax that is easier to write correctly than C
Explicitly model mutability as a type
Access to any C library!
And we still get:
state-of-the-art GC
full Scala FP capabilities
pattern matching
errors & exceptions
immutable data structures, etc
That's what Scala Native is
- but what are the best use cases for it?
- how does it relate to Graal, to Rust, to Go?
- what does it need to fulfill its potential?
Repeating my caveat - this isn't a fight
Comparing to highlight different capabilities
Understanding where tools complement each other
Rust
Go
OCaml/Reason
Graal
Scala.js
JVM Scala
C-level performance and memory safety without GC overhead
a discipline of explicit ownership of values, which can be intrusive
not an FP language (no monads) but it's type system has a Hindley-Milner flavor
has unsafe features for C interop etc but increasingly discouraged
hot take - the Rust community is challenging the Von Neumann model, but has yet to demonstrate generality
Multiple threads working on large shared data structures is an important domain but not universal
can you build a successful OS or a dynamic language in Rust? can it provide a platform to build on or is it a walled garden?
SN's safe code is higher-level than safe Rust
FP and immutable collections are the heart of Scala
high performance, garbage collected, state of the art concurrent runtime
quirky but powerful built-in slice, map, and channel types
no generics, no subclassing of built-in collections
lingua franca for kubernetes and distributed systems
C interop strongly discouraged
Scala Native and Go have similar profiles - compiled, compact binary, high performance, GC
I think the ergonomics of Scala are better - monad-ish collection operations, chainable error handling
The semantics of channels and goroutines seem like a step back from the actor model, and Go does not empower the community to build abstractions
Go is not for FP
SN is much stronger at C interop than Go
A strict FP language with HM type inference and a great object system as well
Excellent performance, native binaries
JS as a target via bucklescript
Reason provides a more conventional syntax
C interop very similar to SN
Applications in finance and HFT
I *heart* OCaml and Reason (and Standard ML/NJ!)
Block/statement syntax is very helpful for systems programming, because POSIX is imperative
Reason seems to be targeting JS/React/Mobile more than server-side/CLI/etc
Risk of Reason/OCaml fragmentation
The Scala ecosystem is larger than the OCaml world
My additions:
embedded
Embedded processors have gotten fast recently!
STM32H747XI - dual core ARM, 480Mhz with DSP
Can run Lua or Python or Tensorflow
Can do DSP on general-purpose devices in idiomatic C
Hardware support is always a challenge but doable
Shadaj Laddad has a 32-bit branch of SN that has run on ARM
A lot of work focusing on embedded would be a strategic decision for the dev team and community
SN could be really great here - flipping bits is the essence of embedded programming, we can beat Rust, Go, OCaml here on ergonomics and performance
WebAssembly is surprisingly analogous to embedded - 32 bit, resource constrained
SN uses LLVM already so not a ton more work - Shadaj has demonstrated running SN code on his branch
Hannes Rutz has run SN in a Web Audio oscillator unit
SN has an advantage here because it includes its own GC
Watch Seb's talk on wasm as a SJS target - worth thinking through how SJS and SN complement each other here
SN's ergonomics are ideal for wasm's linear memory
To the extent that wasm conquers the world, it demonstrates that the C/linear memory model is general!
As cloud/distributed computing technology matures, it is increasingly effective to run stateless computations in a "serverless" modality, scheduled ad hoc on free resources, as pioneered by AWS Lambda
Creating a new process or VM to serve a request is very sensitive to startup time, binary size, memory usage etc.
Typically billed by seconds of execution x memory usage
SN's low footprint is very hard to beat here
Two new classes of hardware peripherals:
1. Non-volatile memory
Two new classes of hardware peripherals:
2. High-speed (10Gbps) NIC
Two new classes of hardware peripherals:
1. Non-volatile memory
2. High-speed (10Gbps) NIC
A language and platform that can build novel abstractions on top of plain linear memory is ideally positioned for these changes
I feel like we could build something genuinely groundbreaking with SN and this class of hardware.