ããã«ã¡ã¯ JavaScriptããã°ã©ãã³ã°èšèªãå®è£
ããããã®ã¬ã€ãã®ç¿»èš³ã®ç¬¬3éšã§ããPLãã¥ãŒããªã¢ã«ã玹ä»ããŸãã
翻蚳è
ãã
ç¬èªã®ããã°ã©ãã³ã°èšèª-λèšèª ïŒå
ã®èšèª -λanguageïŒãäœæããŸãã äœæã®ããã»ã¹ã§ã¯ãååž°éäžãã³ã³ãããŒã«è»¢éã¹ã¿ã€ã«ãåºæ¬çãªæé©åææ³ãªã©ãå€ãã®è峿·±ãææ³ã䜿çšããŸãã ã€ã³ã¿ããªã¿ã®2ã€ã®ããŒãžã§ã³ãäœæãããŸã-éåžžã®ã€ã³ã¿ããªã¿ãšCPSã€ã³ã¿ããªã¿ãJavaScriptã®ãã©ã³ã¹ã³ã³ãã€ã©ã
ãªãªãžãã«ã®äœè
ã¯ãæåãªUglifyJSã©ã€ãã©ãªïŒJSã³ãŒããæå°åããã³ãã©ãŒãããããããã®ããŒã«ïŒã®äœè
ã§ããMihai Bazonã§ãã
PSã€ã³ã¿ããªã¿ãšã³ã³ãã€ã©ã«ãã°ããããŸãïŒ a() && b()ãŸãã¯a() || b()ãããªåŒã§ a() || b()äž¡æ¹ã®éšåãåžžã«å®è¡ãããŸãã ãã¡ãããããã¯ééã£ãŠa() &&æŒç®åã«å¯ŸããŠa()åœã§ãããã ||ã«å¯ŸããŠåœã§ã¯ãªãããã§ãã ã b()å€ã¯äœã®åœ¹å²ãæãããŸããã ãããä¿®æ£ããã®ã¯é£ãããããŸããã
CPSã€ã³ã¿ãŒããªã¿ãŒ
λèšèªã«ã¯2ã€ã®æ¬ ç¹ããããŸãã
- ååž°ã¯JSã¹ã¿ãã¯ã«éå®ãããŠãããããã«ãŒããå®è¡ããéåžžã®æ¹æ³ã¯ãããŸããã
 - ã€ã³ã¿ããªã¿ã¯é
ããããååž°ã¯éåžžã«é
ããªããŸãã
 
ããã§ãã€ã³ã¿ããªã¿ãããã«é
ããªããšããäºå®ã«æ³šæãæãããšãªããæåã®æ¬ é¥ãä¿®æ£ããŸãã ã€ã³ã¿ããªã¿ããç¶ç¶æž¡ãã¹ã¿ã€ã«ãïŒCPSïŒã¹ã¿ã€ã«ã«æžãæããŸãã
ãç¶ç¶è»¢éããšã¯
ããã¯åžžã«NodeJSã§è¡ããŸãïŒ
 fs.readFile("file.txt", "utf8", function CC(error, data){  
ãã¹ãŠã®ã¹ãããã§ãç¶è¡ããå¿
èŠããããšãã«åŒã³åºãããã³ãŒã«ããã¯ããããŸãã ç¶ç¶è»¢éã¹ã¿ã€ã«ã«ãããã³ã³ãããŒã«è»¢éããæç€ºçãã«ãªããŸãreturn ã throw ã breakãŸãã¯continueã¯äœ¿çšããŸããã ã³ãŒãã«ã¯æé»ã®ãžã£ã³ãã¯ãããŸããã forãŸãã¯whileã«ãŒããéåæé¢æ°ã§äœ¿çšããããšãã§ããŸããã ãã®å Žåããªãããã°ã©ãã³ã°èšèªã§ããããå¿
èŠãªã®ã§ããïŒ
ç¶ç¶ãéä¿¡ããã¹ã¿ã€ã«ã§ã³ãŒããæžãã®ã¯é£ãããééããç¯ããããã§ãããã€ã³ã¿ãŒããªã¿ãŒã¯ãã®ã¹ã¿ã€ã«ã§ã®ã¿æžãæããŸãã
evaluate颿°
evaluate颿°ã¯ãåŒïŒASTïŒãã³ã³ããã¹ãïŒç°å¢ïŒãããã³çµæãè¿ããããšãã«åŒã³åºããã颿°ã®3ã€ã®åŒæ°ãåãåããŸãã ã³ãŒãã¯æ¬¡ã®ãšããã§ããåãã©ã°ã¡ã³ãã«ã€ããŠèª¬æããããŸãã
 function evaluate(exp, env, callback) { switch (exp.type) { 
宿°ã«ã€ããŠã¯ããã®å€ãè¿ãã ãã§ãã ãã ãã returnã¯ãããŸããã代ããã«ãã³ãŒã«ããã¯ãåŒã³åºããŠå€ãæž¡ãã ãã§ãã
  case "num": case "str": case "bool": callback(exp.value); return; 
varããŒããåçŽã§ã-ã³ã³ããã¹ããã倿°ãååŸãã颿°ã«æž¡ããŸãïŒ
  case "var": callback(env.get(exp.value)); return; 
assignããŒãã®å Žåãå·Šã®åŒã®å€ïŒ right ïŒãååŸããå¿
èŠããããŸãã ãããè¡ãã«ã¯ã evaluateãåŒã³åºããŠãçµæãååŸãã颿°ãæž¡ããŸãïŒåŒã®å³åŽïŒã ãããŠã倿°å€ã§ã³ãŒã«callbackãåŒã³åºããçŸåšã®ã³ã³ããã¹ãã§å€æ°ãèšå®ããŸãã
  case "assign": if (exp.left.type != "var") throw new Error("Cannot assign to " + JSON.stringify(exp.left)); evaluate(exp.right, env, function(right){ callback(env.set(exp.left.value, right)); }); return; 
binaryããŒãã§ãã»ãŒåãã§ãããããã§ã¯ãŸãleftãã£ãŒã«ãã®å€ãååŸããæ¬¡ã«rightãã£ãŒã«ãã®å€ãååŸããå¿
èŠããããŸãã æ¬¡ã«ãã³ãŒã«ããã¯ãåŒã³åºããŠãæäœã®çµæãæž¡ããŸãã
  case "binary": evaluate(exp.left, env, function(left){ evaluate(exp.right, env, function(right){ callback(apply_op(exp.operator, left, right)); }); }); return; 
letããŒãã¯ããè€éã«èŠããŸãããå®éã¯åçŽã§ãã ããã€ãã®å€æ°ããããŸãã ãããã®defãã£ãŒã«ãïŒåæå€ïŒã¯ç©ºãããããŸããããã®å Žåã falseèšå®ãfalse ã ãã ããå€ãããå Žåã¯ããããååŸããããã«evaluateååž°çã«åŒã³åºãå¿
èŠããããŸãã
NodeJSã以åã«äœ¿çšããããšãããå Žåã¯ããã§ã«ãããäœåºŠãå®è¡ããŠããŸãã ã³ãŒã«ããã¯ã®ããã for䜿çšããããšã¯ã§ããŸããããããã£ãŠããããã®åŒãäžåºŠã«1ã€ãã€è§£éããå¿
èŠããããŸãïŒ evaluate颿°ãéåæãšèããŠãã ããïŒã 以äžã®loop颿°ïŒããã«åŒã³åºãããŸãïŒã¯ãåŠçããå¿
èŠã®ããå®çŸ©ãšã³ã³ããã¹ããååŸããŸãïŒ
- ãã®æ°ã倿°ã®æ°ïŒ 
vars.length ïŒã«çããå Žåãããã¯ãåŒã®æ¬äœãå®è¡ã§ããããã«ãã¹ãŠã®åŒãæ¢ã«å®çŸ©ããŠããããšãæå³ããŸãã ä»åã¯callbackåŒã³åºããªãããšã«æ³šæããŠãã ããããããããããevaluateã«æž¡ãããããæ¬¡ã«ãããåŒã³åºããŸãã - ãã®æ°ãå°ãªãå ŽåãçŸåšã®å®çŸ©ãèšç®ããã³ã³ããã¹ããã³ããŒããåã«
loop(scope, i + 1)ãåŒã³åºã颿°ãæž¡ãå¿
èŠããããŸãã
  case "let": (function loop(env, i){ if (i < exp.vars.length) { var v = exp.vars[i]; if (v.def) evaluate(v.def, env, function(value){ var scope = env.extend(); scope.def(v.name, value); loop(scope, i + 1); }); else { var scope = env.extend(); scope.def(v.name, false); loop(scope, i + 1); } } else { evaluate(exp.body, env, callback); } })(env, 0); return; 
 
åãšåæ§ã«ãã©ã ãããŒãã¯å¥ã®é¢æ°ã§åŠçãããŸãã
  case "lambda": callback(make_lambda(env, exp)); return; 
ifãè§£éããããã«ããŸãæ¡ä»¶ãè§£éããŸãã falseã§ãªãå Žåã thenåŒãè§£éããå¥ã®ã±ãŒã¹ã§ã¯elseããã°ãããè§£éãããªãå Žåã¯falseè¿ãfalse ã åãšåãããã«ã thenããã³elseã«å¯ŸããŠãåŒã®ãå®è¡åŸã«å®è¡ããã¢ã¯ã·ã§ã³ããšããŠcallbackãæž¡ãã ãelse ã
  case "if": evaluate(exp.cond, env, function(cond){ if (cond !== false) evaluate(exp.then, env, callback); else if (exp.else) evaluate(exp.else, env, callback); else callback(false); }); return; 
progããŒãã¯letããŒããšåæ§ã«è§£éãããŸãããã³ã³ããã¹ããã³ããŒããã倿°ãå®çŸ©ãããããå¿
èŠããªããšããéãããããŸãã ãŸããåŒçªå·ãåãåãloop颿°ããããŸãã prog.lengthã«çããå Žåããã¹ãŠã®åŒã宿ããæåŸã®åŒã®çµæãè¿ãããŸãïŒãreturnããšããèšèã§ãã³ãŒã«callbackãåŒã³åºãcallback ïŒã æåŸã®å€ãloop颿°ãžã®åŒæ°ãšããŠæž¡ãããšã§èŠããŠããããšã«æ³šæããŠãã ããïŒæåã¯æ¬äœã空ã®å Žåã¯falseã§ãïŒïŒ
  case "prog": (function loop(last, i){ if (i < exp.prog.length) evaluate(exp.prog[i], env, function(val){ loop(val, i + 1); }); else { callback(last); } })(false, 0); return; 
åcallããŒãã®func ã funcãè§£éããå¿
èŠãããããã¹ãŠã®åŒæ°ãé çªã«äžŠãã§ããŸãã ãŸãã letãŸãã¯progãšåãåçã§åäœããloop颿°ãããloop ãçµæãšããŠé
åãäœæãããšããéãããããŸãïŒ
  case "call": evaluate(exp.func, env, function(func){ (function loop(args, i){ if (i < exp.args.length) evaluate(exp.args[i], env, function(arg){ args[i + 1] = arg; loop(args, i + 1); }); else { func.apply(null, args); } })([ callback ], 0); }); return; 
ããŠãæšæºçãªãšã³ãã£ã³ã°ïŒäœããã¹ããããããªãå Žåã¯ãäŸå€ãã¹ããŒããŸãïŒ
  default: throw new Error("I don't know how to evaluate " + exp.type); } } 
äžèšã®åcaseãreturnããŒã¯ãŒãã§çµããããšã«æ°ä»ããããããŸããã ãã ããæ»ãå€ã¯ãããŸãããçµæã¯åžžã«callback颿°ã«æž¡ããcallback ã
æ°ããmake_lambda颿°
ãã®ã€ã³ã¿ãŒããªã¿ãŒã§ã¯ããã¹ãŠã®é¢æ°ã¯æåã®åŒæ°ãšããŠãç¶ç¶ããåãåããŸããçµæãæž¡ãããã«åŒã³åºãå¿
èŠã®ãã颿°ã§ãã ãã®åŸã¯éåžžã®é¢æ°åŒæ°ã§ãã make_lambda颿°ã®æ°ããã³ãŒãã¯æ¬¡ã®make_lambdaã§ãã
 function make_lambda(env, exp) { if (exp.name) { env = env.extend(); env.def(exp.name, lambda); } function lambda(callback) { var names = exp.vars; var scope = env.extend(); for (var i = 0; i < names.length; ++i) scope.def(names[i], i + 1 < arguments.length ? arguments[i + 1] : false); evaluate(exp.body, scope, callback); } return lambda; } 
圌ã¯ããã»ã©éãã¯ãããŸããã æ°ããã³ã³ããã¹ãã«æ°ãã倿°ã远å ããŸãã ãŸããæåã®åŒæ°ãcallbackã§ããããšãèæ
®ããå¿
èŠãããcallback ã æåŸã«ã evaluate颿°ã¯æ°ããã³ã³ããã¹ãã§é¢æ°ã³ãŒããå®è¡ããããã«äœ¿çševaluateãŸãããåè¿°ã®ããã«ã callbackãæž¡ãcallback ã
ã¡ãªã¿ã«ãããã¯forã«ãŒãã䜿çšããå¯äžã®å Žæã§ãã ããã¯ã callããŒããåŠçããããšãã«åŒæ°å€ããã§ã«èšç®ãããŠããããã§ãã
ãã€ãã£ã颿°
ãã®ã€ã³ã¿ãŒããªã¿ãŒã§ã¯ããã€ãã£ã颿°ã¯æåã®åŒæ°ãšããŠcallbackãåãåããŸãã ãã€ãã£ã颿°ãå®çŸ©ãããšãããããèŠããŠããå¿
èŠããããŸãã æ°ããã€ã³ã¿ãŒããªã¿ãŒã®ãµã³ãã«ã³ãŒããæ¬¡ã«ç€ºããŸãã
 var code = "sum = lambda(x, y) x + y; print(sum(2, 3));"; var ast = parse(TokenStream(InputStream(code))); var globalEnv = new Environment();  
å°ããã¹ã
ãã£ããããæ°ãããäžåºŠèšç®ããŠã¿ãŸãããã
 fib = λ(n) if n < 2 then n else fib(n - 1) + fib(n - 2); time( λ() println(fib(10)) ); 
ãããã27çªç®ã®æ°å€ãèŠã€ããããšãããšãã¹ã¿ãã¯ãªãŒããŒãããŒãçºçããŸãã äžè¬ã«ãã¹ã¿ãã¯ã¯ä»ãããã£ãšéãæé·ããŠããã®ã§ããã£ããããæ°ãã«ãŠã³ãã§ããã®ã¯12æ¥ãŸã§ã§ãïŒå°ãªããšãç§ã®ãã©ãŠã¶ãŒã§ã¯ïŒã ããã¯ããŸãè¯ããªãã®ã§ãã©ãã«ãä¿®æ£ããå¿
èŠããããŸãã
ã¹ã¿ãã¯ãä¿è·ããŸã
CPSã€ã³ã¿ãŒããªã¿ãŒã§ã¯ãã€ã³ã¿ãŒããªã¿ãŒã¯åžžã«é¢æ°ãååž°çã«åŒã³åºããçµæãè¿ããªããããã¹ã¿ãã¯ã¯éåžžã«éãæé·ããŸãã ã€ã³ã¿ããªã¿ã«returnãŸããããããã¯å¿
èŠã§ãããéåžžã«æ·±ãååž°ã®å Žåããããã«å°éããããšã¯ãããŸããã
ã¹ã¿ãã¯ãéåžžã«åçŽãªããã°ã©ã ãã©ã®ããã«èŠãããæ³åããŠã¿ãŸãããã æ¬äŒŒã³ãŒãã衚瀺ããŸãããããã§ã¯äœã®åœ¹å²ãæãããªãããã env远å ããŸããã§ããã
 print(1 + 2 * 3);  
æåŸã®åŒã³åºãã®åŸã®ã¿ãç¡é§ãªreturné·ãã·ãŒã±ã³ã¹ã¯ã¹ã¿ãã¯ãæžãããŸãã åçŽãªããã°ã©ã ã«éåžžã«å€ãã®ã¹ã¿ãã¯é åã䜿çšããå Žåã fib(13)äœãèµ·ãããæ³åããã®ã¯å°é£ã§ãã
ã¹ã¿ãã¯ä¿è·
æ°ããã€ã³ã¿ãŒããªã¿ãŒã§ã¯ãã¹ã¿ãã¯ã¯å¿
èŠãããŸããã äœããã®åŒãåŒæ°ãšããŠæž¡ãããcallbackã§çºçããåŸã«è¡ãå¿
èŠããããã¹ãŠã ããã§è³ªåããããŸãïŒJavaScriptãã¹ã¿ãã¯ãããã³ããããããšãå¯èœã«ãããã©ããªãã§ããããã ãã®åŸãã¹ã¿ãã¯ããããããããšãç¡éã«æ·±ãååž°ãæ©èœããŸãã
ãããè¡ãããšãã§ããGUARD颿°ããããšæ³åããŠã¿ãŸãããã 2ã€ã®å€ãååŸããŸããåŒã³åºã颿°ãšãæž¡ãå¿
èŠã®ããåŒæ°ã§ãã ã¹ã¿ãã¯ãæ·±ãããå Žåãã¹ã¿ãã¯ãã¯ãªã¢ããæž¡ããã颿°ãåŒã³åºããŸãã å¥ã®ã±ãŒã¹ã§ã¯ã圌女ã¯äœãããŸããã
æ°ãã颿°ã䜿çšããŠã以äžã«ç€ºãããã«ã€ã³ã¿ãŒããªã¿ãŒãæžãæããŸãã ç§ã¯åã
ã®ã±ãŒã¹ã«ã€ããŠã³ã¡ã³ãããŸãããåã«èª¬æããã³ãŒãããããŸããã GUARD颿°ã䜿çšããŠããŸãã
 function evaluate(exp, env, callback) { GUARD(evaluate, arguments); switch (exp.type) { case "num": case "str": case "bool": callback(exp.value); return; case "var": callback(env.get(exp.value)); return; case "assign": if (exp.left.type != "var") throw new Error("Cannot assign to " + JSON.stringify(exp.left)); evaluate(exp.right, env, function CC(right){ GUARD(CC, arguments); callback(env.set(exp.left.value, right)); }); return; case "binary": evaluate(exp.left, env, function CC(left){ GUARD(CC, arguments); evaluate(exp.right, env, function CC(right){ GUARD(CC, arguments); callback(apply_op(exp.operator, left, right)); }); }); return; case "let": (function loop(env, i){ GUARD(loop, arguments); if (i < exp.vars.length) { var v = exp.vars[i]; if (v.def) evaluate(v.def, env, function CC(value){ GUARD(CC, arguments); var scope = env.extend(); scope.def(v.name, value); loop(scope, i + 1); }); else { var scope = env.extend(); scope.def(v.name, false); loop(scope, i + 1); } } else { evaluate(exp.body, env, callback); } })(env, 0); return; case "lambda": callback(make_lambda(env, exp)); return; case "if": evaluate(exp.cond, env, function CC(cond){ GUARD(CC, arguments); if (cond !== false) evaluate(exp.then, env, callback); else if (exp.else) evaluate(exp.else, env, callback); else callback(false); }); return; case "prog": (function loop(last, i){ GUARD(loop, arguments); if (i < exp.prog.length) evaluate(exp.prog[i], env, function CC(val){ GUARD(CC, arguments); loop(val, i + 1); }); else { callback(last); } })(false, 0); return; case "call": evaluate(exp.func, env, function CC(func){ GUARD(CC, arguments); (function loop(args, i){ GUARD(loop, arguments); if (i < exp.args.length) evaluate(exp.args[i], env, function CC(arg){ GUARD(CC, arguments); args[i + 1] = arg; loop(args, i + 1); }); else { func.apply(null, args); } })([ callback ], 0); }); return; default: throw new Error("I don't know how to evaluate " + exp.type); } } 
ç¡å颿°ã®å Žåã GUARD颿°ã«æž¡ãããšãã§ããããã«ååã远å ããå¿
èŠããããŸããã ç§ã¯ååCC ïŒ current continuation ïŒã䜿çšããŸããã arguments.callee䜿çšã§ããŸãããããã¯æä»£é
ãã®APIã§ãã
ãŸãã make_lambdaã®åã倿ŽïŒ
 function make_lambda(env, exp) { if (exp.name) { env = env.extend(); env.def(exp.name, lambda); } function lambda(callback) { GUARD(lambda, arguments);  
GUARDã®å®è£
GUARDéåžžã«ç°¡åã§ãã ãã£ãŒãã³ãŒã«ããæãåºãã«ã¯ïŒ äŸå€ã䜿çšããŸãã ãããè¡ãã«ã¯ãããã«å€ãã®ååž°ãå®è¡ã§ããããšã瀺ãã°ããŒãã«å€æ°ããããŸãã ãŒãã«éããå Žåã Continuationãªããžã§ã¯ããã¹ããŒããŸãContinuationãªããžã§ã¯ãã«ã¯ã颿°ãšåŒæ°ã®ç¶ç¶ããããŸãã
 var STACKLEN; function GUARD(f, args) { if (--STACKLEN < 0) throw new Continuation(f, args); } function Continuation(f, args) { this.f = f; this.args = args; } 
ãããŠæåŸã«ã Continuationãªããžã§ã¯ãããã£ããããã«ãŒããå¿
èŠã§ãã ãã¹ãŠãæ©èœããããã«ããã®ã«ãŒããä»ããŠã€ã³ã¿ãŒããªã¿ãŒãåŒã³åºãå¿
èŠããããŸãã
 function Execute(f, args) { while (true) try { STACKLEN = 200; return f.apply(null, args); } catch(ex) { if (ex instanceof Continuation) f = ex.f, args = ex.args; else throw ex; } } 
Execute颿°ã¯ãåŒã³åºããã颿°ãšãã®åŒæ°ãåãå
¥ããŸãã ã«ãŒãå
ã§æ©èœããŸããã颿°ãã¹ã¿ãã¯ãªãŒããŒãããŒãªãã§æ©èœããå Žåã¯ãããã«åæ¢ããããã«æ³šæããŠãã ããã STACKLEN ãã«ãŒãã®å埩ãéå§ãããã³ã«ãªã»ãããããŸãã å€200-ããåããŸãã Continuationãªããžã§ã¯ãããã£ãããããšãæ°ãã颿°ãšåŒæ°ã眮ãæããŠãã«ãŒããç¶ç¶ããŸãã ãŸããäŸå€ã®ãããã¹ã¿ãã¯ã¯ã¯ãªã¢ãããç¶è¡ã§ããŸãã
ã€ã³ã¿ãŒããªã¿ãŒãéå§ããã«ã¯ã Executeã䜿çšããŸãã
 Execute(evaluate, [ ast, globalEnv, function(result){ console.log("*** Result:", result); }]); 
ãã¹ã
fib颿°ãæ©èœããããã«ãªããŸããïŒ
 fib = λ(n) if n < 2 then n else fib(n - 1) + fib(n - 2); time( λ() println(fib(20)) );  
æ®å¿µã§ããã fib(27)ãèŠã€ããããšãããšãéåžžã®éèš³ã®çŽ4åã®æéãããããŸãã ããããä»ã§ã¯ç¡éã®ååž°ããããŸãã
 sum = λ(n, ret) if n == 0 then ret else sum(n - 1, ret + n); # compute 1 + 2 + ... + 50000 time( λ() println(sum(50000, 0)) ); # 1250025000, ~700ms 
ãã¡ãããλèšèªã¯JavaScriptãããã¯ããã«é
ãã§ãã æ³åããŠã¿ãŠãã ããã倿°ãžã®ã¢ã¯ã»ã¹ã¯ãã¹ãŠã Environmentãªããžã§ã¯ããçµç±ããŸãã ã€ã³ã¿ãŒããªã¿ãŒãæé©åããããšããŠãæå³ããããŸãããããã©ãŒãã³ã¹ã倧å¹
ã«åäžããããšã¯ãããŸããã ããã©ãŒãã³ã¹ãæ¹åããããã®1ã€ã®è§£æ±ºçããããŸããλèšèªãJSã§ã³ã³ãã€ã«ããŸãããããç§ãã¡ãããããšã§ãã æåã«ãCPSã€ã³ã¿ãŒããªã¿ãŒã§äœãã§ãããèŠãŠã¿ãŸãããã
ç¶ç¶
ã€ã³ã¿ãŒããªã¿ãŒã¯ç¶ç¶ãéä¿¡ããã¹ã¿ã€ã«ã§åäœãããã¹ãŠã®é¢æ°ïŒÎ»èšèªé¢æ°ãšãã€ãã£ãJS颿°ã®äž¡æ¹ïŒã¯ãçµæãè¿ãæåã®åŒæ°ãšããŠç¶ç¶é¢æ°ãåãåããŸãïŒãã®åŒæ°ã¯ãλèšèªé¢æ°ã«ã¯è¡šç€ºãããŸããããJavaScript颿°ã«å¿
èŠã§ãïŒã
倿°callbackã¯ãããã°ã©ã å
šäœã®ç¶ç¶ãæå³ãcallback ã ããã°ã©ã ã次ã«è¡ããã¹ãŠã®ããšã ãã®å€æ°ããçŸåšã®ç¶ç¶ããŸãã¯ã³ãŒãå
ã®kãšåŒã³ãŸãã
ãŸããç¶ç¶ãè¡ããªãå Žåãããã°ã©ã ã¯åæ¢ããŸãã ãšã«ããmake_lambdaã¯ç¶ç¶ãåŒã³åºããããλèšèªãããããè¡ãããšã¯ã§ããŸããã ãããããã€ãã£ã颿°ãããããè¡ãããšãã§ããŸãã ã©ã®ããã«æ©èœãããã瀺ã颿°ãäœæããŸããã
 globalEnv.def("halt", function(k){}); 
ããã¯äœãããªã颿°ã§ãã 圌女ã¯ç¶ç¶ïŒ k ïŒãåãåããŸããããããåŒã³åºããŸããïŒ
 println("foo"); halt(); println("bar"); 
çµè«ïŒ
 foo 
halt()åŒã³åºããåé€ãããšãåºåã¯æ¬¡ã®ããã«ãªããŸãïŒ foo / bar / ***Result: false ïŒæåŸã®printlnãfalseè¿ãããïŒã ãã ãã halt() fooã®ã¿ãåºåãããŸãã * halt()ã¯ç¶ç¶ãåŒãèµ·ãããªããããçµæããããŸããããããã£ãŠã evaluateããã«æž¡ããã³ãŒã«ããã¯-æåå***Resultã衚瀺ããã³ãŒã«ããã¯ã¯åŒã³åºãããŸããã evaluateãåŒã³åºãã颿°ã¯ãããã°ã©ã ã忢ããããšã«æ°ä»ããŸããã NodeJSã§ã¯ãããã¯è¶³å
ã§ã®ã·ã§ããã«ãªããŸãã
ãã©ãŠã¶ã忢ããã«ïŒãããã£ãŠãæããªã«ãŒããªãã§ïŒããã°ã©ã ã忢ããsleepe颿°ãå¿
èŠã ãšæ³åããŠãã ããã ãã€ãã£ã颿°ã䜿çšããŠãããç°¡åã«å®è£
ã§ããŸãã
 globalEnv.def("sleep", function(k, milliseconds){ setTimeout(function(){ Execute(k, [ false ]);  
å°ãäžäŸ¿ãªã®ã¯ã setTimeoutãã³ãŒã«ããã¯ãåŒãèµ·ããããããContinuationãã¹ããŒãã誰ããã£ããããªãããã Executeã䜿çšããå¿
èŠãããããšã§ãã äœ¿çšæ¹æ³ã¯æ¬¡ã®ãšããã§ãã
 let loop (n = 0) { if n < 10 { println(n); sleep(250); loop(n + 1); } }; println("And we're done"); 
çµè«ïŒ
 0 1 2 3 4 5 6 7 8 9 And we're done ***Result: false 
åã©ã€ã³éã«ããããªé
å»¶ãããããšã«æ³šæããŠãã ããã å
ã®èšäºã§ãã®ã³ãŒããå®è¡ããŠã¿ãŠãã ãã ã
ç¶ç¶ã®æåéä¿¡ã䜿çšããããšãé€ããŠãããã¯ãã§ã«éåžžã®JavaScriptã§ã¯å®è¡ã§ããªãããšã§ãïŒãŸãã forã«ãŒãfor䜿çšããããšãã§ããŸããïŒã
 (function loop(n){ if (n < 10) { console.log(n); setTimeout(function(){ loop(n + 1); }, 250); } else { println("And we're done");  
NodeJS APIãλèšèªã§äœ¿çšããæ¹æ³ãæ³åããŠãã ããã
 globalEnv.def("readFile", function(k, filename){ fs.readFile(filename, function(err, data){  
äœ¿çšæ³ïŒ
 copyFile = λ(source, dest) { writeFile(dest, readFile(source)); }; copyFile("foo.txt", "bar.txt"); 
ãããŠãããã¯ãã¹ãŠéåæã«æ©èœããŸãã ãã以äžã®ã³ãŒã«ããã¯å°çã
ããè峿·±ãäŸã次ã«ç€ºããŸãã æ¬¡ã®ãã€ãã£ã颿°ãäœæããŸããã
 globalEnv.def("twice", function(k, a, b){ k(a); k(b); }); 
ããã°ã©ã ã¯ã2ã€ã®åŒæ°aãšbãåãåããåŒæ°ããšã«1åãç¶ç¶ã2ååŒã³åºããŸãã ãããåŒã³åºããŠã¿ãŸãããïŒ
 println(2 + twice(3, 4)); println("Done"); 
çµè«ïŒ
 5 Done ***Result: false 6 Done ***Result: false 
ãããŸã§ã«ç¶ç¶ã®åãæž¡ãã«åãçµãã ããšããªããããã®ã³ãŒããèªåã§çè§£ããããšããå Žåãçµè«ã¯å¥åŠã§ãã ã¡ãã£ãšãããã³ãïŒããã°ã©ã ã¯1åèµ·åããŸãããçµæã¯2åè¿ãããŸãã
æ±åïŒ CallCC
ãã€ãã£ã颿°ãæžããšããç§ãã¡ã¯ãã€ãŠç«ã§éãã§ããŸããã λèšèªã§ã¯ãçŸåšã®ç¶ç¶ã®å®è¡ãäžæããæ¹æ³ã¯ãããŸããã CallCCã¯ãã®åé¡ã®è§£æ±ºã«åœ¹ç«ã¡ãŸãã ååã¯ãSchemeã®é¢æ°ã®çç¥åœ¢ã§ã-call call-with-current-continuation ïŒéåžžãSchemeã§ã¯call / ccãšåŒã°ããŸãïŒã
 globalEnv.def("CallCC", function(k, f){ f(k, function CC(discarded, ret){ k(ret); }); }); 
çŸåšã®ç¶ç¶ããλèšèªããçŽæ¥åŒã³åºãããšãã§ãã颿°ã«å
·äœåããŸãã ãã®é¢æ°ã¯ãå
ã®discardedç¶ç¶ãç¡èŠãã代ããã«CallCC颿°ã«æž¡ãããç¶ç¶ãåŒã³åºããŸãã
ãã®é¢æ°ã䜿çšããŠãïŒãã€ãã£ã颿°ã§ã¯ãªãããã§ã«Î»èšèªã§ïŒïŒäŸå€ããéå§ããŠreturnã§çµäºããreturnãŸã§èããããªãã£ãå®è¡ãããŒå¶åŸ¡æŒç®åã®å€§ããªã»ãããå®è£
ã§ããŸãã æåŸããå§ããŸãããã
å®è£
return
 foo = λ(return){ println("foo"); return("DONE"); println("bar"); }; CallCC(foo); 
çµè«ïŒ
 foo ***Result: DONE 
foo颿°ã¯ãJavaScriptããã®returnãšåãåŒæ°ãåãåããŸãïŒãã ããéåžžã®é¢æ°ãšããŠåŒã³åºãããŸãïŒã çŸåšã®ç¶ç¶ïŒ 'bar'ãåºåããïŒãã¹ããããã颿°ãçµäºããŠãæå®ãããå€ïŒ "DONE"ïŒãè¿ããŸãã ãã¡ãããããã¯CallCCã䜿çšããŠé¢æ°ãåŒã³åºããç¶ç¶ãreturnãšããŠæž¡ãå Žåã«ã®ã¿æ©èœããŸãã ããã®ã©ãããŒãäœæã§ããŸãã
 with-return = λ(f) λ() CallCC(f); foo = with-return(λ(return){ println("foo"); return("DONE"); println("bar"); }); foo(); 
çµè«ïŒ
 foo ***Result: DONE 
ãã®ã¡ãœããã®å©ç¹ã¯ã fooãåŒã³åºããšãã«CallCCã䜿çšããå¿
èŠããªããªãããšã§ãã ãã¡ãããåŒæ°ãšããŠreturnãåãå
¥ãããã with-return颿°ã䜿çšããå¿
èŠããªãå Žåã¯è¯ãã§ãããããèšèªã§ã¯æ§æã·ã¥ã¬ãŒãçŽæ¥è¿œå ããæ¹æ³ã¯ãããŸããããã®ãããå°ãªããšãããŒãµãŒã倿Žããå¿
èŠããããŸãïŒLispã®å Žåã¯+1ïŒã
é·ç§»
ããšãã°ãä¹ç®ã84ã«ãªãå Žåã100ãŸã§ã®æ£ã®æŽæ°ã®ãã¹ãŠã®ãã¢ãæ€çŽ¢ããããã°ã©ã ãäœæããå¿
èŠããããŸããããã¯é£ããäœæ¥ã§ã¯ãªããããã«è§£æ±ºãã2ã€ã®ãã¹ããããã«ãŒããæ³åã§ããŸãããããã§ã¯å¥ã®æ¹æ³ã説æããŸãã guessãšfail 2ã€ã®é¢æ°ãäœæããŸãã æåã®äººã¯çªå·ãéžã³ã2çªç®ã®äººã¯åœŒå¥³ã«ãå¥ã®çªå·ã詊ããŠãã ããããšäŒããŸãã æ¬¡ã®ããã«äœ¿çšããŸãã
 a = guess(1);  
:
 fail = λ() false; guess = λ(current) { CallCC(λ(k){ let (prevFail = fail) { fail = λ(){ current = current + 1; if current > 100 { fail = prevFail; fail(); } else { k(current); }; }; k(current); }; }); }; a = guess(1); b = guess(a); if a * b == 84 { print(a); print(" x "); println(b); }; fail(); 
, , a , 84 , b , 84 / a . guess : start end â . , .
try catch Common Lisp
â catch throw . :
 f1 = λ() { throw("foo", "EXIT"); print("not reached"); }; println(catch("foo", λ() { f1(); print("not reached"); }));  
catch(TAG, function) , , TAG ', function .throw(TAG, value) , , TAG ' , value .
:
  
äŸïŒ
  
catch , , , . , , , CallCC . , . " " â â . , , , catch / throw , .
. , catch . , throw , , catch , . äŸïŒ
 exit = false;  
çµè«ïŒ
 After catch After catch ERROR: No more catch handlers! 
'After catch' , 'ERROR: No more catch handlers!'. - , 'After catch' , . , '' , catch . , 'XXX' catch , throw , catch .
( , .)
CallCC (, , CallCC ). , â CallCC .
Yield
, , yield :
 foo = with-yield(λ(yield){ yield(1); yield(2); yield(3); "DONE"; }); println(foo());  
yield , . , return . , , yield , .
:
 fib = with-yield(λ(yield){ let loop (a = 1, b = 1) { yield(b); loop(b, a + b); }; });  
fib . . yield . , , 50 , 50 .
, , , , .
, .
yield , , . , , return . , , yield , yield , . , . :
 with-yield = λ(func) {  
yield , . , , , "DONE".
, . , - , , 4 :
 println(foo()); foo(); 
.
â1: yield
, , , , yield ( kyld , , ) :
  yield(2); yield(3); "DONE"; 
yield ? yield , , yield . , . , yield return , :
 with-yield = λ(func) { let (return, yield) { yield = λ(value) { CallCC(λ(kyld){ func = kyld; return(value);  
, , yield , yield ( ). yield .
, , println(foo()) :
 with-yield = λ(func) { let (return, yield) { yield = λ(value) { CallCC(λ(kyld){ func = kyld; return(value); }); }; λ(val) { CallCC(λ(kret){ return = kret; func(val || yield); }); }; }; }; foo = with-yield(λ(yield){ yield(1); yield(2); yield(3); "DONE"; }); println(foo()); println(foo()); println(foo()); 
, . , print(foo()); foo() . , println(foo()) ? ...
â2: WTF?
. : , foo() . , ? â .
 println(foo());  
with-yield :
  func(val || yield);  
yield , , #... . , , ( "DONE" ), , "DONE" , . foo() , "DONE" . :
 println(foo()); println("bar"); println(foo()); println(foo()); foo(); 
: 1, bar, 2, 3, DONE, bar, DONE, bar, ... .
func - , . , "no more continuations" :
  val = func(val || yield); func = λ() "NO MORE CONTINUATIONS"; kret(val); 
:
 with-yield = λ(func) { let (return, yield) { yield = λ(value) { CallCC(λ(kyld){ func = kyld; return(value); }); }; λ(val) { CallCC(λ(kret){ return = kret; val = func(val || yield); func = λ() "NO MORE CONTINUATIONS"; kret(val); }); }; }; }; foo = with-yield(λ(yield){ yield(1); yield(2); yield(3); "DONE"; }); println(foo()); println(foo()); println(foo()); println(foo()); 
, :
 1 2 3 DONE NO MORE CONTINUATIONS NO MORE CONTINUATIONS NO MORE CONTINUATIONS ***Result: false 
1, 2, 3, DONE , "NO MORE CONTINUATIONS" . , :
 print("A. "); println(foo()); print("B. "); println(foo()); print("C. "); println(foo()); print("D. "); println(foo());  
, : , , , , "B." .
, yield , :
 with-yield = λ(func) { let (return, yield) { yield = λ(value) { CallCC(λ(kyld){ func = kyld; return(value); }); }; λ(val) { CallCC(λ(kret){ return = kret; val = func(val || yield); func = λ() "NO MORE CONTINUATIONS"; kret(val); }); }; }; }; fib = with-yield(λ(yield){ let loop (a = 1, b = 1) { yield(b); loop(b, a + b); }; });  
ãããã« 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733 1134903170 1836311903 2971215073 4807526976 7778742049 12586269025 20365011074 ***Result: false 
 , (, ), " ".
: reset shift
yield : reset shift . " " â , . reset , shift , CallCC .
, reset shift â . reset , shift , reset .
, with-yield :
 with-yield = λ(func) { let (yield) {  
, reset . , , , reset . , . , .
:
 with-yield = λ(func) { let (yield) { yield = λ(val) { shift(λ(k){ func = k; val; }); }; λ(val) { reset( λ() func(val || yield) ); }; } }; foo = with-yield(λ(yield){ yield(1); yield(2); yield(3); "DONE"; }); println(foo());  
reset shift
, . . . , , , . Scheme ( â Oleg Kiselyov ). .
, ( CallCC ) . :
 var pstack = []; function _goto(f) { f(function KGOTO(r){ var h = pstack.pop(); h(r); }); } globalEnv.def("reset", function(KRESET, th){ pstack.push(KRESET); _goto(th); }); globalEnv.def("shift", function(KSHIFT, f){ _goto(function(KGOTO){ f(KGOTO, function SK(k1, v){ pstack.push(k1); KSHIFT(v); }); }); }); 
, reset , shift _goto , â . . _goto ( KGOTO ). , f ( CallCC ) - KGOTO , . , f , , KGOTO , , .
reset ( KRESET ) _goto(th) . , , , _goto . , , KGOTO KRESET .
, shift KGOTO , KGOTO pstack , SK , , shift ( shift â KSHIFT ). SK â â ( k1 ) . , shift2 , , pstack.push(k1); ïŒ
 println(reset(λ(){ 1 + shift( λ(k) k(k(2)) ); })); println(reset(λ(){ 1 + shift2( λ(k) k(k(2)) ); })); 
çµè«ïŒ
 4 3 ***Result: false 
shift ( k ), reset . â 1 shift :
 1 + [?] 
k , ? . â k(k(2)) . 2 , k(2) 3. , k(3) 3 , 4.
shift2 : k(2) .
CallCC
, , CallCC , . , JS, , , . , CallCC , .
â goto , ( ):
 pstack = NIL; goto = false; reset = λ(th) { CallCC(λ(k){ pstack = cons(k, pstack); goto(th); }); }; shift = λ(f) { CallCC(λ(k){ goto(λ(){ f(λ(v){ CallCC(λ(k1){ pstack = cons(k1, pstack); k(v); }); }); }); }); }; let (v = CallCC( λ(k){ goto = k; k(false) } )) { if v then let (r = v(), h = car(pstack)) { pstack = cdr(pstack); h(r); } }; 
â !