New page: When writing a language like JavaScript or Enso one necessarily ends up writing builtins - basic language operations that cannot be expressed in the language itself...

New page

When writing a [[language]] like [[JavaScript]] or [[Enso]] one necessarily ends up writing [[builtins]] - basic [[language]] operations that cannot be expressed in the [[language]] itself, but need to be built into the interpreter/compiler of the [[language]].

== Granularity ==

All languages have concept of [[builtins]], however the different is the '''granularity'''.
In general the [[builtins]] internals should ''not be visible'' in the stack trace.
Not only it makes the stack traces needlessly long,
but it also complicates [[debugging]] as one has to step thru all these ''builtin levels''.

For example [[Java]] and [[JavaScript]] do the same thing quite differently. In [[JavaScript]] all the [[builtins]] are written in other language than [[JavaScript]]. As such the ''stacktraces are sane'':
<source lang="javascript">
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].filter (x => {
if (x == 5) throw "Error:"+x;
})
</source>

yields (when executed by [[GraalVM]].js, but also any other engine):
<source lang="bash">
$ graalvm-17/bin/js f.js
Error:5
at <js> :=>(f.js:2:70-77)
at <js> :program(f.js:1-3:0-81)
</source>

However compare the same example written in [[Java]]:
<source lang="java">
class F {
public static void main(String... args) {
java.util.List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).stream().filter(x -> {
if (x == 5) throw new RuntimeException("Error:" + x);
return true;
}).toList();
}
}
</source>
and such an example yields:
<source lang="bash">
$ graalvm-17/bin/java F.java
Exception in thread "main" java.lang.RuntimeException: Error:5
at F.lambda$main$0(F.java:4)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178)
at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)
at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)
at F.main(F.java:6)
</source>

Eleven stacktraces lines instead of two! A lot of boiler plate output instead of two important lines that matter. Who cares there is some ''AbstractPipeline''? Or ''ReferencePipeline''?

Moreover everything gets really complicated when it comes to [[debugging]]. In [[Java]] the debugger has to step thru all these intermediate stacktraces. That's not the case of [[JavaScript]] - in [[JavaScript]] the ''Step Next'' action goes directly from main code to the body of the lambda function and back. [[JVM]]'s decision to implement basic operations like ''filter'' in [[Java]] itself and not as builtin complicates this all. Some [[IDE]]s are even sad to had to design a special ''stream debugger'' exactly for these purposes.

==== Implications for [[Enso]] Language Designers ====

Thus, when designing [[Enso]] we should ask: Do we want [[Enso]] to be like [[JavaScript]] or like [[Java]]?