You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Typescript compiler supports transforms. A transform typically visits every node in your Typescript program (which are a lot of nodes!) and lets you replace/transform the nodes that match a certain condition.
To know when the compiler should transform a generator function to it's nested then-equivalent, we'll let the user wrap the generator function in another function, blabla like so:
The type parameter (boolean in the example above) refers to the result type of the generator function. In this case, result will have the type Promise<boolean>.
The function blabla will be declared in a .d.ts-file, but it won't have a definition. Instead, the transform will see the call to blabla and will take that as the sign to start transforming. The end result will not contain any call to blabla anymore.
The transform will replace every statement like
constmyValue=yieldmyPromise
with
returnmyPromise.then(myValue=>{// ...})
Whatever comes after the original statement will be moved inside the body of the anonymous function within myPromise.then(..). This way, that code has access to myValue and whatever follows should compile just fine.
The transform will continue by doing the exact same thing (replacing a yield with a corresponding call to the then-method) with whatever follows the original statement.
For other monads
That's brilliant, but so far it only works for a Promise, and for Promises we have the perfectly usable async and await syntax. The trick described above is not limited to Promises though. It should also work for Arrays (where we might use Lodash' flatMap) and Observables (where we might prefer mergeMap). To make it work on all these data types, we'll let the user pass the type class that lets us chain these values together. We'll extend our definition of blabla to something you can use as follows:
The second type parameter of blabla (boolean in the example above) is similar to the first (and only) parameter in the previous definition of blabla. The first type parameter refers to the URI type parameter as described in the documentation.
This is more or less type-safe already. When you pass 'promise' as the first argument to blabla, but you're yielding an Array, the compiler will complain.
But... As discussed, the result of a yield is always any so that's still a challenge to solve.
Transforms, all of them, happen after the checking phase has happened
That means, that our transformed generator function will not be type-checked yet. We could still make a mistake!
At this point, I can think of a number of options:
I can implement a library in which you can type-check the transformed generator function as a separate compilation step that you can run just like you could run your tests or a linter.
The danger of this is that the yield_ will be erased, like blabla. A user that's unaware of this can let the yield_-function escape from the function. It should also be noted that the yield_ cannot be used outside of the direct function scope of our "generator"-function (something that is automatically enfored with yield):
@wernerdegroot , FWIW, there's something like this provided by Do in fp-ts-contrib. This version, while certainly not as succinct or clean as the specialized syntax you're proposing, works with current TS/JS syntax.
Your generator example would look like:
constresult=Do(promiseMonad)// <-- typeclass instance passed here .bindL('num',()=>numberPromise).bindL('str',()=>stringPromise).return(({num, str})=>str.length===num)
Of course, this works with any type for which a monad instance exists.
Also, the type inference in Do is quite strong. Notice how in the example, no type parameters are provided.
This issue is not really an issue, but a request for feedback on something I'm working on.
I am working on a Typescript transformer that will transform something like
to something like
Transforming
The Typescript compiler supports transforms. A transform typically visits every node in your Typescript program (which are a lot of nodes!) and lets you replace/transform the nodes that match a certain condition.
To know when the compiler should transform a generator function to it's nested
then
-equivalent, we'll let the user wrap the generator function in another function,blabla
like so:The type parameter (
boolean
in the example above) refers to the result type of the generator function. In this case,result
will have the typePromise<boolean>
.The function
blabla
will be declared in a.d.ts
-file, but it won't have a definition. Instead, the transform will see the call toblabla
and will take that as the sign to start transforming. The end result will not contain any call toblabla
anymore.The transform will replace every statement like
with
Whatever comes after the original statement will be moved inside the body of the anonymous function within
myPromise.then(..)
. This way, that code has access tomyValue
and whatever follows should compile just fine.The transform will continue by doing the exact same thing (replacing a
yield
with a corresponding call to thethen
-method) with whatever follows the original statement.For other monads
That's brilliant, but so far it only works for a
Promise
, and forPromise
s we have the perfectly usableasync
andawait
syntax. The trick described above is not limited toPromise
s though. It should also work forArray
s (where we might use Lodash'flatMap
) andObservable
s (where we might prefermergeMap
). To make it work on all these data types, we'll let the user pass the type class that lets us chain these values together. We'll extend our definition ofblabla
to something you can use as follows:The second type parameter of
blabla
(boolean
in the example above) is similar to the first (and only) parameter in the previous definition ofblabla
. The first type parameter refers to theURI
type parameter as described in the documentation.This is more or less type-safe already. When you pass
'promise'
as the first argument toblabla
, but you'reyield
ing anArray
, the compiler will complain.But... As discussed, the result of a
yield
is alwaysany
so that's still a challenge to solve.Challenges
As the documentation says:
That means, that our transformed generator function will not be type-checked yet. We could still make a mistake!
At this point, I can think of a number of options:
The danger of this is that the
yield_
will be erased, likeblabla
. A user that's unaware of this can let theyield_
-function escape from the function. It should also be noted that theyield_
cannot be used outside of the direct function scope of our "generator"-function (something that is automatically enfored withyield
):My questions
yield_
) do you prefer?The text was updated successfully, but these errors were encountered: