Kind of new to PureScript… read a lot but first time actually using it.
I have a library using JavaScript arrays as argument, a recursive array, like ["a", ["d", ["c"], "d"]], or in TypeScript type A = string | A[]. First idea I thought was using data A = String | Array A, turned out PureScript is not using JavaScript Arrays directly underneath.
Is there a solution to construct such a recursive JavaScript thing within PureScript.
currently in my code I have to use really dirty code to make it work…
let transformNodes = (xs, leafCtor, listCtor) => {
if (xs instanceof leafCtor) {
// dirty solution to use `.value0`
return xs.value0;
}
if (xs instanceof listCtor) {
return xs.value0.map((x) => transformNodes(x, leafCtor, listCtor));
}
console.log("Unexpected node:", xs);
throw new Error("Unexpected node");
};
exports.writeCirruImpl = (xs) => (options) => (leafCreate) => (listCreate) => {
// dirty way of detecting type
let leafCtor = leafCreate(null).constructor;
let listCtor = listCreate(null).constructor;
let ys = transformNodes(xs, leafCtor, listCtor);
return writer.writeCirruCode(ys, options);
};
foreign import writeCirruImpl ::
CirruNode ->
{ useInline :: Boolean} ->
(String -> CirruNode) ->
(Array CirruNode -> CirruNode) ->
String
writeCirru :: CirruNode -> { useInline :: Boolean } -> String
writeCirru nodes options = writeCirruImpl nodes options CirruLeaf CirruList
Welcome to PureScript!
You seem to have the wrong idea about PureScript data types. PureScript does not support type unions like TypeScript does. Instead, data A = String | Array A creates a type which has two constructors: String and Array A. The equivalent of this in TS would be the following:
type A = AString | AArray A
interface AString {
kind: "string";
}
interface AArray {
kind: "array";
value: A;
}
These are called tagged unions, while something like number | string in TypeScript is called an untagged union.
Since you want to wrap foreign data types in PureScript, you may want to do something like this:
foreign import data A :: Type
fromString :: String -> A
fromString = unsafeCoerce
fromArray :: Array A -> A
fromArray = unsafeCoerce
foo = fromArray [ fromString "a", fromArray [ fromString "b" ] ]
That doesn’t look too ergonomic though. There’s probably another solution that looks better than this one, but I am not too familiar with advanced PureScript FFI, so take my suggestion with a grain of salt.
That’s incorrect. x = [1, 2, 3] will compile down to something like var x = [1, 2, 3] in JS. You just weren’t actually using the Array type in your code.
Since you’re new to PureScript, I would recommend you try out at least some of the resources in https://github.com/purescript/documentation#learning. I especially recommend the PureScript book and Jordan’s reference.
1 Like
I also forgot to say something important: avoid depending on the implementation of PureScript ADTs (i.e. value0, xs instanceof listCtor, etc.). These are internal implementation details and are not intended to be used by FFI functions, so they might be changed between PureScript compiler versions without warning. I realize that you already know that this is “really dirty” code, but I want to emphasize this.
1 Like
exactly, I will replace the dirty code as soon as I find a solution.
I have rough concepts about tagged unions, thanks for examples. I haven’t using fromArray yet, it seems not dynamic enough… trying if there’s anything I can do here.
1 Like
Figured out a less dirty solution, that I transformed my data to nested data with Record type first, since Records are using plain JavaScript fields, now the glue code does not need to know properties like .value0:
newtype FatNode = FatNode { isList :: Boolean, leaf :: String , list :: (Array FatNode) }