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) }