purescript / roadmap

Long-term projects not covered by other issues lists
20 stars 1 forks source link

Generate TypeScript bindings #42

Open paf31 opened 8 years ago

megamaddu commented 8 years ago

@AppShipIt and I were talking about currying the other day.

I proposed something like this: https://gist.github.com/spicydonuts/263e216f238c3bc0dc39f68167c1769b

He took that a little further after looking at BuckleScript's inlining features and proposed something similar which only generated the curried functions as needed.

This makes a lot of sense to me because the compiler is the one thing that knows for sure whether a particular function application needs to be curried. Defaulting to currying is simpler, but as soon as you start looking at optimizing post-compile it gets complicated again. Plus the bundler has way less info at its disposal than the compiler itself.

Anyways, we talked about TypeScript integration a bit too and being able to count on all the basic exported functions to not be curried will make consuming PS from both JS and TS much more straightforward. It would also eliminate the need for extra ceremony like purescript-functions.

garyb commented 8 years ago

Please see purescript/purescript#1686, purescript/purescript#693, purescript/purescript#479 for a sampling of previous discussions about uncurrying. This comment in particular demonstrates a few reasons why uncurrying is extremely difficult, or actually worse in some cases...

If we did want to uncurry the exported interfaces I'd propose an entirely separate tool does that work while generating the typescript definitions, and in fact generates new modules with uncurried members that call the curried members internally. If it's about making JS interop easier, then it's better not to impose the associated issues with uncurrying on all the generated purescript code for the sake of the calls across the boundary.

megamaddu commented 8 years ago

That makes sense. I'll read up!

If it's about making JS interop easier, then it's better not to impose the associated issues with uncurrying on all the generated purescript code for the sake of the calls across the boundary.

Yeah, my hope was that it wouldn't affect the PS code or the concerns of the average PS developer much while also improving FFI, but if that's not the case then it's probably not a good idea.

megamaddu commented 8 years ago

The main issues I have with purescript-functions are..

  1. You have to choose.. you can't expose the JS-friendly EffFn3 * * * * while also keeping the idiomatic PS api. The best you can do is add an extra module with JS-friendly wrappers.
  2. FFI in core, contrib, and 3rd party libs varies greatly.. it'd be nice if they were consistent (another way to fix this might be to emit a compiler warning for foreign imports which don't have a type of FnN or EffFnN (maybe it'd be better to warn for any use of -> in a foreign import so you can still import constants and data declarations without special cases.. same effect though)).
  3. If all the core libs use purescript-functions and purescript-eff-functions, nearly every PS project will have them installed. If every project has them installed and there's special compiler support for them, it seems a little strange to have them in separate libraries.
garyb commented 8 years ago

2 is the same problem with automatic uncurrying actually: it's not clear how un-applied a given function can be, so the result can be strange breaks in the arity.

Also with 2, it is a perfectly good point, but the choice is made sometimes because a curried version is more efficient (e.g. when the foreign import needs to accept a function from a typeclass, as they only need applying once at definition time, and then the "real" function is returned, whereas with runFnX they'd have to be passed every time with the associated dictionary-lookup cost every time).

That's why I think keeping things as they are, and providing an uncurried layer on top of that is preferrable to the other way around. Although we would need more information than psc-bundle has currently to do it right, as you also pointed out.

megamaddu commented 8 years ago

After looking at the issues you linked I agree about just keeping special cases for FnN types. Thoughts on warnings for forgetting to use FnN in foreign imports? What if FnN types were part of the base language like Boolean & EffFnN was part of purescript-eff? And to get really crazy, AffFnN could expect Promises and convert them to Affs 😱haha