Here’s what a type-class based approach would look like:
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
class IntStringArray a
instance intStringArrayInt :: IntStringArray Int
instance intStringArrayString :: IntStringArray String
instance intStringArrayArray :: IntStringArray (Array a)
ps_to_ffi_boundary :: forall a. IntStringArray a => a -> String
ps_to_ffi_boundary = ffi_code
foreign import ffi_code :: forall a. a -> String
main :: Effect Unit
main = do
log $ ps_to_ffi_boundary 4
log $ ps_to_ffi_boundary "foo"
log $ ps_to_ffi_boundary [1, 2, 3]
with FFI code as
"use strict";
exports.ffi_code = function (intStringArray) {
if (Number.isInteger(intStringArray)) {
return "Got an integer";
} else if (typeof intStringArray === 'string' || intStringArray instanceof String) {
return "Got a string";
} else if (Array.isArray(intStringArray)) {
return "Got an array of values";
} else {
throw new Error("This should never happen unless I checked the argument's type incorrectly");
}
};
The outputted JavaScript code would be this:
"use strict";
var $foreign = require("./foreign.js");
var Effect_Console = require("../Effect.Console/index.js");
var IntStringArray = {};
var ps_to_ffi_boundary = function (dictIntStringArray) {
return $foreign.ffi_code;
};
var intStringArrayString = IntStringArray;
var intStringArrayInt = IntStringArray;
var intStringArrayArray = IntStringArray;
var main = function __do() {
Effect_Console.log(ps_to_ffi_boundary()(4))();
Effect_Console.log(ps_to_ffi_boundary()("foo"))();
return Effect_Console.log(ps_to_ffi_boundary()([ 1, 2, 3 ]))();
};
module.exports = {
IntStringArray: IntStringArray,
ps_to_ffi_boundary: ps_to_ffi_boundary,
main: main,
intStringArrayInt: intStringArrayInt,
intStringArrayString: intStringArrayString,
intStringArrayArray: intStringArrayArray,
ffi_code: $foreign.ffi_code
};
So, if there was a special compiler rule that said, “Anything that extends the Erased
type class will have its dictionary removed from compiled output,” would make this idea feasible. I’m not sure whether that can be done or how hard it would be to do that.
If it could be done and was, perhaps this would be the output:
"use strict";
var $foreign = require("./foreign.js");
var Effect_Console = require("../Effect.Console/index.js");
var main = function __do() {
Effect_Console.log($foreign.ffi_code(4))();
Effect_Console.log($foreign.ffi_code("foo"))();
return Effect_Console.log($foreign.ffi_code([ 1, 2, 3 ]))();
};
module.exports = {
main: main,
ffi_code: $foreign.ffi_code
};