How to setup a project with spago to be able to call a purescript function from javascript

spago
ffi
#1

This is a copy of a spago issue. Copied for @hdgarrood

I’m using this setup https://github.com/purescript/spago#get-started-from-scratch-with-parcel-frontend-projects

And run npm run dev.

The purescript manual says i should be able to execute shout(require('Prelude').showNumber)(42);. This doesn’t work though. I tried instead console.log(require('../../output/Prelude')); but this gave an empty object because there are no functions in the prelude javascript file

index.js in Prelude folder:

// Generated by purs version 0.13.6
"use strict";
module.exports = {};

Anyway what i really wanted to do is convert a Set into a List (because a Set structure is a pita to work with in JS). So i did let opt = require('../../output/Data.Set').toUnfoldable(mySet); this works as it’s calling the right function. But it then crashes in this code.

From Data.List

var toUnfoldable = function (dictUnfoldable) {
    return Data_Unfoldable.unfoldr(dictUnfoldable)(function (xs) { // <-- crash point
        return Data_Functor.map(Data_Maybe.functorMaybe)(function (rec) {
            return new Data_Tuple.Tuple(rec.head, rec.tail);
        })(uncons(xs));
    });
};
TypeError: Data_Unfoldable.unfoldr(...) is not a functionclient.e31bb0bc.js:16462:49
    toUnfoldable http://localhost:1234/client.e31bb0bc.js:16462
    toUnfoldable http://localhost:1234/client.e31bb0bc.js:23010
    new http://localhost:1234/client.e31bb0bc.js:23319
    handleAction http://localhost:1234/client.e31bb0bc.js:52021
    $tco_loop http://localhost:1234/client.e31bb0bc.js:33695
    toView http://localhost:1234/client.e31bb0bc.js:33719
    go http://localhost:1234/client.e31bb0bc.js:34053
    go http://localhost:1234/client.e31bb0bc.js:28850
    go http://localhost:1234/client.e31bb0bc.js:28858
    _run http://localhost:1234/client.e31bb0bc.js:24432
    step http://localhost:1234/client.e31bb0bc.js:24510
    drain http://localhost:1234/client.e31bb0bc.js:24268
    enqueue http://localhost:1234/client.e31bb0bc.js:24291
    step http://localhost:1234/client.e31bb0bc.js:24499
    eventListener http://localhost:1234/client.e31bb0bc.js:29240

Data.Set.toUnfoldable looks like this

var toUnfoldable = function (dictUnfoldable) {
    var $63 = Data_List.toUnfoldable(dictUnfoldable);
    return function ($64) {
        return $63(toList($64));
    };
};

How must the project be setup so that the code in the output folder can use each other’s functions? And how can i use just require('Data.Set') rather than working with relative paths?

1 Like
#2

Ok so the first thing is this:

The purescript manual says i should be able to execute shout(require('Prelude').showNumber)(42);

Unfortunately this is no longer true. It’s intended that output/Prelude/index.js has no exports. The reason for this is that if a PureScript module re-exports something, that re-export won’t appear in the generated JavaScript. Instead, in the generated code, things are imported directly from the module where they are defined. Can you point me to where you read this? It probably ought to be updated.

#3

Regarding this:

let opt = require('../../output/Data.Set').toUnfoldable(mySet);

The reason this doesn’t work is that toUnfoldable has the type forall f a. Unfoldable f => Set a -> f a, which means that it is compiled to a function which takes two arguments: one for the Unfoldable f dictionary, and one for the set. It’s possible to call toUnfoldable from JS if you track down the appropriate instance dictionary, but don’t recommend calling constrained functions from JavaScript at all really. When you’re using PureScript functions from JS, it’s safer to use functions without type class constraints. In this case if you know what you want your Set to be converted to, you can pick the type you want on the PureScript side by doing this:

setToArray :: Set a -> Array a
setToArray = Set.toUnfoldable

and then using setToArray from the JS side instead.

1 Like
#4
#5

thanks! You could maybe open an issue or even send a PR if you felt so inclined. I think the instance lives in Data.Show now.

1 Like
Cannot find module when attempting run PureScript function from JavaScript
#6

I.e try shout(require('./output/Data.Show').showNumber) ?

#7

PR in https://github.com/dwhitney/purescript-book/pull/57

1 Like