Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Future.liftXXX #12

Closed
emmanueltouzery opened this issue Aug 23, 2018 · 13 comments
Closed

Add Future.liftXXX #12

emmanueltouzery opened this issue Aug 23, 2018 · 13 comments

Comments

@emmanueltouzery
Copy link
Owner

We want a Future.liftXXX-like function as described there and in the rest of the conversation, and suggested by @qm3ster:
#8 (comment)

the only real hurdle left is the name for the function.

@emmanueltouzery
Copy link
Owner Author

note that GHC haskell also talks about lifted and unlifted types. I get the feeling the use of the 'lift' term in FP is quite loose. I may open a stackoverflow question on the subjet.

@emmanueltouzery
Copy link
Owner Author

emmanueltouzery commented Sep 2, 2018

I finally took the time to ask the question on stackoverflow. Also note, as I wrote there, that prelude.ts currently differentiates between 'lifting' (as in lifting a function returning a Promise to a function returning a Future) versus 'applicative lifting' (as in what @qm3ster -and probably most people- means by lifting). This may be bogus, looking forward to some feedback on stackoverflow.

emmanueltouzery added a commit that referenced this issue Sep 3, 2018
…tionX.liftNullable to Option.liftNullable, FunctionX.liftEither to Either.lift. Thanks @qm3ster for the tip for the implementation. Currently the functions support only up to 5 parameters, when typescript 3.1 comes out we can support any number of parameters (the feature came with TS3.0 but we want to support the two latest versions).
emmanueltouzery added a commit that referenced this issue Sep 3, 2018
@emmanueltouzery
Copy link
Owner Author

I have now committed a version using the Future.lift naming and I think it'll stay through the next release. The naming would then be Future.lift (and also Option.lift, Option.liftNullable and Either.lift) when only the result type is changed. And Future.liftA2 and Future.liftAp (and same for Option and Either) for "functor" or "applicative" lifting where also parameter types are lifted. cc @qm3ster

The implementations currently use overloading and support up to 5 parameters but when TS3.1 is released I'll change them to use the new TS3 feature enabling us to support any number of parameters.

@qm3ster
Copy link

qm3ster commented Sep 4, 2018

Yeah, my usage was definitely wrong.
This was a good refresher - https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/
Since we aren't lifting the function into the plane of the Future monad, but making more of a ramp that can generate Futures when invoked with simple values.

@emmanueltouzery
Copy link
Owner Author

I went through your link quickly, it seems to cover the liftA* functions, I think that bit is relatively universal. So we agree it makes sense to differentiate liftA & liftXXX. And given that, using directly lift doesn't shock you that much anymore, since the other one is liftA*? am I reading you correctly?

@qm3ster
Copy link

qm3ster commented Sep 4, 2018

I don't know, it's still possible to make a real lift for arbitrary uncurried function.
Something like

const lift = fn => (...args) => Future.collect(args).map(arr => fn.apply(undefined, arr))

@emmanueltouzery
Copy link
Owner Author

emmanueltouzery commented Sep 5, 2018

So you're saying liftAn for functions taking any numbers of parameters of any types. But can the type be expressed in TS?

Note that prelude has liftAp:

const lifted = Option.liftAp((x:{a:number,b:number,c:number}) => x.a+x.b+x.c);
lifted({a:Option.of(5), b:Option.of(6), c:Option.of(3)});
=> Option.of(14)

@emmanueltouzery
Copy link
Owner Author

Not sure if you really meant liftAn though, i'm not familiar with the collect function.

@qm3ster
Copy link

qm3ster commented Sep 5, 2018

liftA2 is crucial for functions of type a1=>a2=>result, but when we apply all arguments at once, the one I provided is sufficient. The current liftAp seems like a workaround of that using argument baggies.

We can type is with overloads for different lengths, with the baseline case being the too losely typed

const lift = <A extends any[], R>(fn: (...args: A) => R) => (
  ...args: Future<A[number]>[]
) => Future.collect(args).map(arr => fn.apply(undefined, arr) as R)

until Mapped Tuples are available.

@emmanueltouzery
Copy link
Owner Author

I see... With mapped tuples (currently just a proposal) we could get a typed version of liftAp without having to go through an intermediate object. We could type-transform each parameter from the parameter list in the result function type.

As it is now, we could probably also have a version which would support up to n parameters through overloads (for a finite n). I'm not interested in the loosely typed option. I'm ok to have a finite n for the current lift (which you called liftXXX), because I know (thanks to you) that TS3.0 will allow us type-safe unlimited number of parameters. So the overloads are a very short term measure.

But I'm less interested to keep it like that for a longer time (as would be with this lift, liftAp replacement). You use the feature, reach the limit in the number of parameters, then get frustrated (though in theory the user could add the extra overload typedef himself I guess). From this point of view, liftAp seems like a decent, and fully type-safe, workaround for the time being. Although yes, it is verbose.

@emmanueltouzery
Copy link
Owner Author

I have to say, It is really fascinating the speed at which the TS typesystem is improving. Thanks for opening me a window into some of these recent & ongoing developments!

@qm3ster
Copy link

qm3ster commented Sep 7, 2018

Its merit is that it's "Just Javascript", but neither Typescript nor flow actually let me express myself.
I don't know what to switch to, though. ReasonML? PureScript? ClojureScript? Fable?!

@emmanueltouzery
Copy link
Owner Author

yes... it has limitations. I did some things in ghcjs/reflex, unfortunately I wouldn't advise it. I would probably pick purescript, but for the foreseeable future I'll have to use something like typescript at work, I hope to make it as nice as possible with prelude.ts.

Just this weekend I was playing with type-level programming (one commit, branch) in typescript, would actually be useful for prelude.ts, but I had to give up as it's advised against and would hurt build time badly.

In the end, typescript not perfect, but still worth pursuing and getting better. Anyway, I'll close this bug now, I'm happy with the API as it is in master!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants