@@ -293,3 +293,64 @@ exports._liftEff = function (nonCanceler, e) {
293293 return nonCanceler ;
294294 } ;
295295}
296+
297+ exports . _tailRecM = function ( isLeft , f , a ) {
298+ return function ( success , error ) {
299+ return function go ( acc ) {
300+ var result , status , canceler ;
301+
302+ // Observes synchronous effects using a flag.
303+ // status = 0 (unresolved status)
304+ // status = 1 (synchronous effect)
305+ // status = 2 (asynchronous effect)
306+ while ( true ) {
307+ status = 0 ;
308+ canceler = f ( acc ) ( function ( v ) {
309+ // If the status is still unresolved, we have observed a
310+ // synchronous effect. Otherwise, the status will be `2`.
311+ if ( status === 0 ) {
312+ // Store the result for further synchronous processing.
313+ result = v ;
314+ status = 1 ;
315+ } else {
316+ // When we have observed an asynchronous effect, we use normal
317+ // recursion. This is safe because we will be on a new stack.
318+ if ( isLeft ( v ) ) {
319+ go ( v . value0 ) ;
320+ } else {
321+ try {
322+ success ( v . value0 ) ;
323+ } catch ( err ) {
324+ error ( err ) ;
325+ }
326+ }
327+ }
328+ } , error ) ;
329+
330+ // If the status has already resolved to `1` by our Aff handler, then
331+ // we have observed a synchronous effect. Otherwise it will still be
332+ // `0`.
333+ if ( status === 1 ) {
334+ // When we have observed a synchronous effect, we merely swap out the
335+ // accumulator and continue the loop, preserving stack.
336+ if ( isLeft ( result ) ) {
337+ acc = result . value0 ;
338+ continue ;
339+ } else {
340+ try {
341+ success ( result . value0 ) ;
342+ } catch ( err ) {
343+ error ( err ) ;
344+ }
345+ }
346+ } else {
347+ // If the status has not resolved yet, then we have observed an
348+ // asynchronous effect.
349+ status = 2 ;
350+ }
351+ return canceler ;
352+ }
353+
354+ } ( a ) ;
355+ } ;
356+ } ;
0 commit comments