ããã«ã¡ã¯ 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); } };
â !