Skip to content

Instantly share code, notes, and snippets.

@recmo
Last active February 2, 2017 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save recmo/1a02121d39ee337fb81fc18e735a0d9e to your computer and use it in GitHub Desktop.
Save recmo/1a02121d39ee337fb81fc18e735a0d9e to your computer and use it in GitHub Desktop.
Async FizzBuzz — implemented using *only* callbacks
// Async FizzBuzz -- implemented using only callbacks
//
// node --stack-size=100000 ./async-fizzbuzz.js
//
// Wrap process.stdout.write
const print = (message, callback) =>{
process.stdout.write(message, callback)
}
// Scott numeral constructors
const Z = (callback)=>{callback((a,b)=>{a()})}
const S = (n, callback)=>{callback((a,b)=>{b(n)})}
// Numeral constants
const n0 = (a,b)=>{a()}
const n1 = (a,b)=>{b(n0)}
const n2 = (a,b)=>{b(n1)}
const n3 = (a,b)=>{b(n2)}
const n4 = (a,b)=>{b(n3)}
const n5 = (a,b)=>{b(n4)}
const n6 = (a,b)=>{b(n5)}
const n7 = (a,b)=>{b(n6)}
const n8 = (a,b)=>{b(n7)}
const n9 = (a,b)=>{b(n8)}
const n10 = (a,b)=>{b(n9)}
const if_zero = (n, then, else_)=>{n(
()=>{then()},
(r)=>{else_()}
)}
const if_equal = (m, n, then, else_)=>{m(
()=>{if_zero(n, then, else_)},
(mr)=>{n(
else_,
(nr)=>{if_equal(mr, nr, then, else_)}
)}
)}
const add = (m, n, callback)=>{m(
()=>{callback(n)},
(mr)=>{S(n, (sn)=>{add(mr, sn, callback)})}
)}
const sub = (m, n, callback, error)=>{n(
()=>{callback(m)},
(nr)=>{m(
error,
(mr)=>{sub(mr, nr, callback, error)}
)}
)}
const mul = (m, n, callback)=>{m(
()=>{Z(callback)},
(mr)=>{mul(mr, n, (r)=>{add(r, n, callback)})}
)}
const divmod = (m, n, callback)=>{
sub(m, n,
(mr)=>{divmod(mr, n, (q, r)=>{
S(q, (sq)=>{callback(sq, r)})
})},
()=>{callback(n0, m)})
}
const divides = (m, n, then, else_)=>{
divmod(m, n, (q, r)=>{if_equal(r, n0, then, else_)})
}
// Decimal output
const print_decimal = (n, callback, error)=>{
if_equal(n, n0, ()=>{print("0", callback)}, ()=>{
if_equal(n, n1, ()=>{print("1", callback)}, ()=>{
if_equal(n, n2, ()=>{print("2", callback)}, ()=>{
if_equal(n, n3, ()=>{print("3", callback)}, ()=>{
if_equal(n, n4, ()=>{print("4", callback)}, ()=>{
if_equal(n, n5, ()=>{print("5", callback)}, ()=>{
if_equal(n, n6, ()=>{print("6", callback)}, ()=>{
if_equal(n, n7, ()=>{print("7", callback)}, ()=>{
if_equal(n, n8, ()=>{print("8", callback)}, ()=>{
if_equal(n, n9, ()=>{print("9", callback)}, error
)})})})})})})})})}) // LISP flashbacks
}
const print_number = (n, callback)=>{
divmod(n, n10, (q, r)=>{
if_zero(q, ()=>{
print_decimal(r, callback)
}, ()=>{
print_number(q, ()=>{
print_decimal(r, callback)
})
})
})
}
// Loops
// for needs more callbacks.
// So far, we have only used anonymous functions. It would
// be a shame to stop that practice here. Therefore, I present
// you a fixed-point combinator in pure CPS:
const for_ = (start, end, func, callback)=>{
((rec, n, end, func, callback)=>{
rec(rec, n, end, func, callback)
})((rec, n, end, func, callback)=>{
func(n, ()=>{
if_equal(n, end, callback, ()=>{
S(n, (sn)=>{
rec(rec, sn, end, func, callback)
})
})
})
}, start, end, func, callback)
}
// We are coming to the main show
const fizzbuzz_number = (n, callback)=>{mul(n3, n5, (n15)=>{
divides(n, n15, ()=>{print("FizzBuzz\n", callback)}, ()=>{
divides(n, n3, ()=>{print("Fizz\n", callback)}, ()=>{
divides(n, n5, ()=>{print("Buzz\n", callback)}, ()=>{
print_number(n, ()=>{print("\n", callback)})
})})})
})}
const fizzbuzz = (callback)=>{mul(n10, n10, (n100)=>{
for_(n1, n100, fizzbuzz_number, callback)
})}
fizzbuzz(()=>{})
@recmo
Copy link
Author

recmo commented Jan 31, 2017

SegFaults nodejs/v8 while processing 91. Bugreports: nodejs v8

@recmo
Copy link
Author

recmo commented Feb 1, 2017

Updated the gist to use a fixed-point combinator and async process.stdout.write. It is now 100% CPS.

@recmo
Copy link
Author

recmo commented Feb 1, 2017

In the new revision, the crash disappears, but it reappears if you do something complicated:

Replace the last line fizzbuzz(()=>{}) with:

mul(n10, n10, (n100)=>{mul(n100, n100, ()=>{})})

And nodejs crashes again. This hints that the crash is not related to IO.

Interestingly, if you replace the last line with these two lines:

fizzbuzz(()=>{})
mul(n10, n10, (n100)=>{mul(n100, n100, ()=>{})})

Then the program prints 1 and then crashes. So something about the second line happens during execution of the first. I suspect this is either the optimizer, or the two lines are speculatively executed in parallel.

@recmo
Copy link
Author

recmo commented Feb 1, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment