F# via fable doesn’t run in nashorn yet, but could

art yerkes
Nov 11, 2018 · 2 min read

Recently, in order to give a coworker an easier way to interact with some code we deliver as javascript, I ported something from bucklescript to fable so that it’d be visible in a .net IDE. Admittedly, the result is pretty swank. Since F# branched from ocaml linguistically, it wasn’t a huge effort (roughly 1 full day of work), but something interesting happened running the resulting javascript on nashorn (note, a late addition here is that I notice that this is mainly due to the es5ification from babel).

Constructing unions in F# would sometimes work and sometimes not, whereas the code ran correctly in node. I had previously tried this and, not wanting to do a deep dive into why fable doesn’t run on nashorn, didn’t investigate very far. Now I know why it happens. Note: the function below isn’t exactly like the one that calls Union, but it clearly illustrates the difference between node and nashorn that makes the bug possible,

arty@clotho:~/dev/corda-web/backend/contract$ jjs
jjs> function f(tag,name) {
if (!tag) {
return Array.prototype.slice.call(arguments);
} else {
return f.call.apply(
f, [f,tag-1,name].
concat(Array.prototype.slice.call(arguments))
);
}
}
jjs> f(0,'hi there')
0,hi there
jjs> f(1,'hi there')
0,hi there,1,hi there
jjs> f(2,'hi there')
0,hi there,1,hi there
jjs> f(3,'hi there')
0,hi there,1,hi there

Here’s the same thing on node:

> f(0,’hi there’) 
f(0,’hi there’)
[ 0, ‘hi there’ ]
> f(1,’hi there’)
f(1,’hi there’)
[ 0, ‘hi there’, 1, ‘hi there’ ]
> f(2,’hi there’)
f(2,’hi there’)
[ 0, ‘hi there’, 1, ‘hi there’, 2, ‘hi there’ ]

So I finally get what’s happening here: union constructors delegate to a Union function to actually construct, slicing off a tag and a name and passing on a reconstituted parameter set to Union.

Weirder still, slightly changing the definition changes the outcome in jjs:

jjs> function f(tag,name) { 
var x = Array.prototype.slice.call(arguments);
if (!tag) {
return Array.prototype.slice.call(arguments);
} else {
return f.call.apply(
f, [f,tag-1,name].
concat(Array.prototype.slice.call(arguments)));
}
}
jjs> f(2,’hi there’)
0,hi there,1,hi there,2,hi there
jjs>

I’ve determined that what I see in nashorn isn’t systematic, but inconsistent, and I believe it’s due to caching, note that order matters:

jjs> function f…
jjs> f(0,’hi there’)
0,hi there
jjs> f(1,’hi there’)
0,hi there,1,hi there
jjs> f(2,’hi there’)
0,hi there,1,hi there

and

jjs> function f…
jjs> f(2,'hi there')
0,hi there,1,hi there,2,hi there
jjs> f(1,'hi there')
0,hi there,1,hi there
jjs> f(0,'hi there')
0,hi there

Happily, it’s possible to work around this particular thing by making fields a named parameter of Union, rather than collecting it at both levels (and it’s more efficient too). I don’t know what else is lurking.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store