evaluatorã®å帰å®è£ âCPSåâContinuationã®de-functionalizeâã«ã¼ãå
ããã°ã©ãã³ã°è¨èªã®ã¤ã³ã¿ããªã¿ãå®è£ ããã¨ããæ§ææ¨ã«ãããã¦å帰ã§æ¸ãã»ããåºæ¬çã«æ¥½ã ãããããå¤ãã®å®è£ ã§å帰å¼ã³åºãã¯åæ°ã«å¶éããã£ããå¦çãé ãã£ãããããããã§ãå帰å¼ã³åºãåã®evalãã«ã¼ãã§è¨è¿°ããåæ©ãã§ããããã ããããªãã«ã¼ãã§æ¸ããã¨ããã¨ãã¤ã³ã¿ããªã¿ã³ã¼ããããããããªããªããããã°ãå ¥ããããã
ããã§ã«ã¼ããä½ãæ¦ç¥ã¨ãã¦ã
- å帰å¼ã³åºãã§å®è£
- å帰å¼ã³åºãã®Continuation Passing Style(CPS)å
- CPSå½¢å¼ã¯æ«å°¾å¼ã³åºãå½¢å¼ã§ããã
- Continuationã®de-functionalizeãã¤ã¾ãContinuationã®é¢æ°è¡¨ç¾ããã¼ã¿ãªãã¸ã§ã¯ãã§ãããã
- ã¯ãã¼ã¸ã£ãæ示çãªãã¼ã¿ãªãã¸ã§ã¯ãã«ãã
- æ«å°¾å¼ã³åºãé¢æ°ã®ã«ã¼ãå
ããããã°ãã«ã¼ãå½¢å¼ã¤ã³ã¿ããªã¿ãç´ ç´ã«å¯¾å¿ä»ããã§ãããã¯ãã¼ã¸ã£ã使ããªãã®ã§ãJavaã¨ãã§ãå®è£ ããããããã
å®è£ è¨èªã¯javascriptãããã§ã®å¯¾è±¡è¨èªã¯å½¢ç¡ãã©ã ãå¼ï¼å®æ°(JavaScriptãªãã¸ã§ã¯ã)ã
è¨èªä»æ§
è¨èªã¯ãé¢æ°(EFunc)ã¨é¢æ°é©ç¨(ECall)ã¨å¤æ°(EVar)ã§æ§æãããåç´å½¢ç¡ãã©ã ãå¼ã«ãJavaScriptãªãã¸ã§ã¯ãã«ãããå®æ°(EVal)ãå ãããã®ã
// AST expr EFunc = function EFunc(param, body) { this.param = param; this.body = body; }; ECall = function ECall(func, arg) { this.func = func; this.arg = arg; }; EVar = function EVar(name) { this.name = name; }; EVal = function EVal(value) { this.value = value; }
è¨èªã®æ§ææ¨ã¯ã以ä¸ã®ä¾ã®ããã«æ¸ãã
// (\n->n) 10 new ECall(new EFunc("n", new EVar("n")), new EVal(10))
æ§æç³
é¢æ°ã¯å¼æ°ã¯ç¢ºå®ã«ã²ã¨ã¤ã«ãªã£ã¦ããããå¤æ°å®ç¾©ããªããããã§è¤æ°å¼æ°ãå¤æ°å®ç¾©ã«å¯¾å¿ããæ§æç³ãç¨æãã¦ã¿ãã
sfunc = function sfunc(params, body) { if (params.length == 1) return new EFunc(params[0], body); var param = params[0]; var params = params.slice(1); return new EFunc(param, sfunc(params, body)); }; scall = function scall(func, args) { if (args.length == 1) return new ECall(func, args[0]); var arg = args[args.length-1]; var args = args.slice(0, args.length-1); return new ECall(scall(func, args), arg); }; slet = function slet(name, value, body) { return new ECall(new EFunc(name, body), value); }; svar = function svar(name) { return new EVar(name); }; sval = function sval(value) { return new EVal(value); }; sunit = function sunit() { return EVal(undefined); };
ããã使ãã¨ä»¥ä¸ã®ãããªä¾ããããã
// k = \x->\y->x; k 10 20 slet("k", sfunc(["x", "y"], svar("x")), scall(svar("k"), [sval(10), sval(20)]))
ãããå¼æ°ãªãã¯å¯¾å¿ãã¦ãªãããå帰é¢æ°ããããªããããã®ã¸ãã¯ä¸»é¡ã§ã¯ãªãã®ã§ãã®ãããã§ã¨ãã¦ããã(å¼æ°ãªãé¢æ°ã¯ãé¢æ°ã¯æé»ã®å¼æ°ãå¿ ãä¸ã¤æã¡ãé©ç¨æã¯ä½è¨ãªå¼æ°(ãã¨ãã°unit)ã渡ãããã«ãããã¨ã§å¯è½ãå帰é¢æ°ãç¸äºå¼ã³åºãã¯yã³ã³ããã¼ã¿ã«ãããæ§æ(ERec(name, body)ã¨ã)ã追å ãã)
å帰çevaluator
ãã¼ã¿æ§é ã®å®ç¾©
evaluatorãä½ãã«ããããå¼ãè©ä¾¡ããçµæã¨ãã¦ã®ãå¤ãã¨ããããã®å¤ãå¼æ°ã¨ãã¦ä¿æãããç°å¢(ãã¬ã¼ã )ããã¾ãç¨æããã
Env = function Env(parent) { this.vars = {}; this.parent = parent; this.put = function put(name, value) { this.vars[name] = value; }; this.get = function get(name) { if (this.vars.hasOwnProperty(name)) { return this.vars[name]; } else { if (this.parent == null) { debug("var not found: " + name); return sunit(); } return this.parent.get(name); } }; }; // Value VVal = function VVal(value) { this.value = value; }; VFunc = function VFunc(env, param, body) { this.env = env; this.param = param; this.body = body; };
ç°å¢ã¯ç¶æ¿æ©è½ã¤ãè¾æ¸ããã®è¨èªã®å ´åãå¤ã¯å®æ°ã¨é¢æ°ã®äºã¿ã¤ãã«ãã¦ããã
å帰çevaluator
å帰çevaluator
// version 1: naive evaluator simpleEvaluate = function simpleEvaluate(expr, env) { if (expr instanceof EFunc) { return new VFunc(env, expr.param, expr.body); } if (expr instanceof ECall) { var func = simpleEvaluate(expr.func, env); // ToBe Lazy: func = eager(func) var arg = simpleEvaluate(expr.arg, env); // ToBe Lazy: arg = new VLazy(expr.arg, env); if (func instanceof VFunc) { var newEnv = new Env(func.env); newEnv.put(func.param, arg); //debug(func.param + "=" + arg); return simpleEvaluate(func.body, newEnv); } else if (func instanceof VVal) { return valueApply(func, arg); } } if (expr instanceof EVar) { return env.get(expr.name); } if (expr instanceof EVal) { return new VVal(expr.value); } };
evaluatorã¯åæ§æè¦ç´ ã§æ¯ãåããããããã«ãã£ãå¦çããã¦å¤ãè¿ãããã®å㯠evaluator::Expr->Env->Value ã«ãªããECallã®ããã«æ§æè¦ç´ ã§ã¡ã³ãã¼ã«è¦ç´ ãæã¤ãã®ã¯ãå帰çã«evaluatorãé©ç¨ãã¦ãã®çµæã使ããã¨ã«ãªããEFuncã¯ãå¤ã¨ãã¦ç°å¢ãä¿æãããVFuncãè¿ãã ãã§ãããä¸æ¹ECallã§ã¯ããã®VFuncã§ä¿æãããç°å¢ã«å¼æ°ã追å ãã¦ããã£ã®å¼ãå®è¡ãããã®ã«ãªãããããã¬ãã·ã«ã«ã¹ã³ã¼ãã®å®ç¾ã«ãªã£ã¦ãããVFuncã®ç°å¢ã§ã¯ãªããevaluatorã«æ¸¡ã£ãç°å¢ã親ç°å¢ã¨ãã¦ä½¿ãã¨ããã¤ãããã¯ã¹ã³ã¼ãã«ãªããç°å¢ã®å®è£ ãã ãã§ã両æ¹è¦ããããªå®è£ ãä½ããã
(ToDo Lazyã³ã¡ã³ãã¯non-strictãªè©ä¾¡ã§å®è£ ããã¨ãã®å¤æ´ç¹)
unboxing
ECallã®è©ä¾¡ã§ã¯ãé¢æ°ãVFuncã®ã¨ãã¨ãå®æ°VValã®ã¨ãããããVValã®ã¨ãã¯javascripté¢æ°ãå ¥ã£ã¦ãã¦ãããå¼ã³åºãä»çµã¿ãå¿ è¦ã«ãªãã以ä¸ã¯ãã®ä»çµã¿ã
// unboxing valueApply = function valueApply(func, arg) { var funcObj = func.value; if (typeof funcObj != "function") { return func; } if (funcObj.length > 1) { funcObj = curry(funcObj, []); } var funcArg = lambda2Js(arg); // ToBe Lazy: funcArg = function () { return lambda2Js(arg); } return new VVal(funcObj(funcArg)); }; lambda2Js = function lambda2Js(value) { // ToBe Lazy: value = eager(value); if (value instanceof VVal) { return value.value; } else if (value instanceof VFunc) { return function lambdaFunc(arg) { var func = new EFunc(value.param, value.body); var expr = new ECall(func, new EVal(arg)); var ret = runEvaluator(expr, value.env); return lambda2Js(ret); }; } };
valueApplyã§ã¯ãå¼æ°å´ã¯VValãVFuncã§ããããããjavascriptã®ãªãã¸ã§ã¯ãã«ãã¦javascripté¢æ°ã«ãããå¿ è¦ããããVValã®ã¨ãã¯ä¸ã«å ¥ã£ã¦ããã®ã«ãªããVFuncã®ã¨ãã¯functionã«ãã¦æ¸¡ãã¦ãããVFuncããjavascriptã®functionã«ããã«ã¯ãåã«å¼æ°ããEValãVFuncãæã¤å¼ã¨ç°å¢ã使ã£ã¦ECallãä½ããevaluatorãå¼ã³åºãã¦ããã®çµæãå帰çã«javascriptãªãã¸ã§ã¯ãã«ããã°ããã
ã¡ãªã¿ã«ä»¥éã®å¤æã§ã¯ãvalueApplyããã®ä¸ã®runEvaluatorã®æ¸ãæãã¾ã§ã¯è¡ã£ã¦ããªããããããevaluatorã®æ¸ãæãã¨åæ§ã«è¡ããã
å帰çâCPSçã¸ã®æ¸ãæã
CPSçã®evaluatorã®åã¯ç¬¬ä¸å¼æ°ã«Continuationé¢æ°ãåãåãExpr->Env->Cont->Valueã«ãªããContåã¯åã®çµæãåãåã£ã¦æçµçµæãè¿ãé¢æ°Value->Valueã ã
段é1: é¢æ°å¼æ°ã®å¤æ´æ¸ãæã
ã¾ããé¢æ°ãcpsEvaluate(expr, env, cont)ã«æ¸ãæããã
// æªå®æ: å¼æ°contã®è¿½å ã®ã¿ cpsEvaluate = function cpsEvaluate(expr, env, cont) { if (expr instanceof EFunc) { return new VFunc(env, expr.param, expr.body); } if (expr instanceof ECall) { var func = cpsEvaluate(expr.func, env, ...); var arg = cpsEvaluate(expr.arg, env, ...); if (func instanceof VFunc) { var newEnv = new Env(func.env); newEnv.put(func.param, arg); //debug(func.param + "=" + arg); return cpsEvaluate(func.body, newEnv, ...); } else if (func instanceof VVal) { return valueApply(func, arg); } } if (expr instanceof EVar) { return env.get(expr.name); } if (expr instanceof EVal) { return new VVal(expr.value); } };
段é2: return v; => return cont(c);æ¸ãæã
次ã«å¤ãç´æ¥è¿ãã¦ããé¨åãcontã«æ¸¡ãã¦è¿ããã®ã«æ¸ãæããã
// æªå®æ: éå帰ã§å¤ãè¿ãã¦ããé¨åreturn v;ãreturn cont(v)ã«ããã cpsEvaluate = function cpsEvaluate(expr, env, cont) { if (expr instanceof EFunc) { return cont(new VFunc(env, expr.param, expr.body)); } if (expr instanceof ECall) { var func = cpsEvaluate(expr.func, env, ...); var arg = cpsEvaluate(expr.arg, env, ...); if (func instanceof VFunc) { var newEnv = new Env(func.env); newEnv.put(func.param, arg); //debug(func.param + "=" + arg); return cpsEvaluate(func.body, newEnv, ...); } else if (func instanceof VVal) { return cont(valueApply(func, arg)); } } if (expr instanceof EVar) { return cont(env.get(expr.name)); } if (expr instanceof EVal) { return cont(new VVal(expr.value)); } };
段é3: å帰å¼ã³åºãã®å¾ç¶é¨åã®é¢æ°å
次ã«å帰å¼ã³åºãã§æ¸¡ãé¨åã®contã¯ããã以éå ¨ä½ããããã§æ»ãå¤ã¯å¼æ°ã«ããé¢æ°ã渡ãããããreturnããããã«æ¸ãæããã
// æªå®æ: var func = cpsEvaluate(expr.func, env, ...);ã®CPSå cpsEvaluate = function cpsEvaluate(expr, env, cont) { if (expr instanceof EFunc) { return cont(new VFunc(env, expr.param, expr.body)); } if (expr instanceof ECall) { //var func = cpsEvaluate(expr.func, env, ...); return cpsEvaluate(expr.func, env, function (func) { var arg = cpsEvaluate(expr.arg, env, ...); if (func instanceof VFunc) { var newEnv = new Env(func.env); newEnv.put(func.param, arg); //debug(func.param + "=" + arg); return cpsEvaluate(func.body, newEnv, ...); } else if (func instanceof VVal) { return cont(valueApply(func, arg)); } }); } if (expr instanceof EVar) { return cont(env.get(expr.name)); } if (expr instanceof EVal) { return cont(new VVal(expr.value)); } };
å¾ç¶é¨åã®é¢æ°åãç¹°ãè¿ãã¦å®æ
ããã«ãã®ä¸ãæ«å°¾å帰å¼ã³åºãå½¢å¼ã«æ¸ãæãããæå¾ã®å帰å¼ã³åºãã§æ¸¡ãcontinuationã«ã¯å¤§æ¬ã®å¼æ°ã®contã渡ãã
// å®æ: å ¨ä½ãCPSå cpsEvaluate = function cpsEvaluate(expr, env, cont) { if (expr instanceof EFunc) { return cont(new VFunc(env, expr.param, expr.body)); } if (expr instanceof ECall) { //var func = cpsEvaluate(expr.func, env, ...); return cpsEvaluate(expr.func, env, function (func) { //var arg = cpsEvaluate(expr.arg, env, ...); return cpsEvaluate(expr.arg, env, function (arg) { if (func instanceof VFunc) { var newEnv = new Env(func.env); newEnv.put(func.param, arg); //debug(func.param + "=" + arg); return cpsEvaluate(func.body, newEnv, cont); } else if (func instanceof VVal) { return cont(valueApply(func, arg)); } }); }); } if (expr instanceof EVar) { return cont(env.get(expr.name)); } if (expr instanceof EVal) { return cont(new VVal(expr.value)); } };
æçµçã«evaluatorã«ããã«ã¯ã大æ¬ã§å¼æ°ããã®ã¾ã¾è¿ãé¢æ°ã渡ãã¦å®è¡ããã
function runEvaluate(expr, env) { return cpsEvaluate(expr, env, function (value) { return value; }); }
ã¨ãªãã
CPSçã®de-functionalize
CPSçã®Continuationã¯å帰é¨åã§å®è¡ç°å¢ãä¿æããé¢æ°ã渡ãã¦ããã
defunctionalizeããã¨ã¯ããã®é¢æ°ãå®è¡ç°å¢ã§å¿ è¦ãªãã®ãä¿æãããã¼ã¿ãªãã¸ã§ã¯ãContã¨ããã®å¦çç³»ã«åé¢ãããã¨ãã¤ã¾ããevaluatorã§ã¯contObjãåãåããCPSçä¸ã®cont(v)ã®é¨åã¯ãapplyCont(contObj, v)ã¨ããå½¢å¼ã«æ¸ãæãããã¨ã«ãªãã
段é1: åconté¢æ°ã«å¯¾å¿ãããã¼ã¿ãªãã¸ã§ã¯ããç¨æ
ã¾ããCPSçã®evaluatorã®contã«æ¸¡ãfunctionã«ãªã£ã¦ããé¨åãå ¨é¨åå¥ã®ãã¼ã¿ãªãã¸ã§ã¯ãã«ããã
// <= function (value) { return value; } CHalt = function CHalt() { }; // <= function (func) { ... } CEvalFunc = function CEvalFunc(arg, env, cont) { this.arg = arg; this.env = env; this.cont = cont; }; // <= function (arg) { ... } CEvalArg = function CEvalArg(func, env, cont) { this.func = func; this.env = env; this.cont = cont; };
ãã¼ã¿ãªãã¸ã§ã¯ããä¿æããã®ã¯ã対å¿ããfunctionã®å¼æ°ä»¥å¤ã®ä½¿ç¨ãã¦ããå¤æ°ãã¹ã¦(ç´ ç´ã«ããã°ãCEvalFuncã®å ´åargãããªãã¦exprãªãã ãã©ãå®é使ã£ã¦ãã®ã¯expr.argã ããªã®ã§ãã·ã§ã¼ãã«ãããã¦ãã¾ã£ãã¨ãããã¨ã§ãCEvalArgãåæ§)ã
段é2: cont(v) => applyCont(cont, v)æ¸ãæã
次ã¯evaluatorã®æ¸ãæããã¾ãã¯cont(v)ãapplyCont(cont, v)ã«ããã
// æªå®æ: cont(v) => applyCont(cont, v) defuncEvaluate = function defuncEvaluate(expr, env, cont) { if (expr instanceof EFunc) { return applyCont(cont, new VFunc(env, expr.param, expr.body)); } if (expr instanceof ECall) { return defuncEvaluate(expr.func, env, function (func) { return defuncEvaluate(expr.arg, env, function (arg) { if (func instanceof VFunc) { var newEnv = new Env(func.env); newEnv.put(func.param, arg); //debug(func.param + "=" + arg); return defuncEvaluate(func.body, newEnv, cont); } else if (func instanceof VVal) { return applyCont(cont, valueApply(func, arg)); } }); }); } if (expr instanceof EVar) { return applyCont(cont, env.get(expr.name)); } if (expr instanceof EVal) { return applyCont(cont, new VVal(expr.value)); } };
段é3: applyContã®éå
次ã«applyCont(cont, value)ããã¨ã®é¢æ°ããç¨æããã
// æªå®æ: ãã¿ã¼ã³ãã applyCont = function applyCont(cont, value) { if (cont instanceof CHalt) { ... } if (cont instanceof CEvalFunc) { ... } if (cont instanceof CEvalArg) { ... } };
段é4: CHaltã®ç½®ãæã
次ã«functioné¨åãContãªãã¸ã§ã¯ãã«å¤ãã¦ãããæ¿ããä¸èº«ã¯applyContã®ä¸èº«ã«ãªãã
ã¾ãã¯å¤§æ¬ã®CHaltããç½®ãæããã
// å®æ: CHaltæ¸ãæã function runEvaluate(expr, env) { // function (value) { return value; } => CHalt return defuncEvaluate(expr, env, new CHalt()); }
// æªå®æ: CHaltã®ç½®ãæã applyCont = function applyCont(cont, value) { if (cont instanceof CHalt) { // <= function (value) { return value; } return value; } if (cont instanceof CEvalFunc) { ... } if (cont instanceof CEvalArg) { ... } };
段é4: CEvalFuncã®ç½®ãæã
CEvalFuncã¸ã®ç½®ãæãã
// å®æ: function (func) {...} => CEvalFunc(arg, env, cont) defuncEvaluate = function defuncEvaluate(expr, env, cont) { if (expr instanceof EFunc) { return applyCont(cont, new VFunc(env, expr.param, expr.body)); } if (expr instanceof ECall) { return defuncEvaluate(expr.func, env, new CEvalFunc(expr.arg, env, cont)); /* return defuncEvaluate(expr.func, env, function (func) { return defuncEvaluate(expr.arg, env, function (arg) { if (func instanceof VFunc) { var newEnv = new Env(func.env); newEnv.put(func.param, arg); //debug(func.param + "=" + arg); return defuncEvaluate(func.body, newEnv, cont); } else if (func instanceof VVal) { return applyCont(cont, valueApply(func, arg)); } }); }); */ } if (expr instanceof EVar) { return applyCont(cont, env.get(expr.name)); } if (expr instanceof EVal) { return applyCont(cont, new VVal(expr.value)); } };
// æªå®æ: CEvalFuncã®ç½®ãæã applyCont = function applyCont(cont, value) { if (cont instanceof CHalt) { // <= function (value) { return value; } return value; } if (cont instanceof CEvalFunc) { // é¢æ°ã®ä¸èº«ã(å¤æ°ããããã¦)ãã®ã¾ã¾åãã // å¤æ°å¯¾å¿: evalã«æ¸¡ãarg => cont.arg, env => cont.env, cont => cont.cont, func => value return defuncEvaluate(cont.arg, cont.env, function (arg) { if (value instanceof VFunc) { var newEnv = new Env(value.env); newEnv.put(value.param, arg); //debug(value.param + "=" + arg); return defuncEvaluate(value.body, newEnv, cont.cont); } else if (func instanceof VVal) { return applyCont(cont.cont, valueApply(value, arg)); } }); } if (cont instanceof CEvalArg) { ... } };
段é5: CEvalArgã®æ¸ãæã
æ®ãã®CEvalArgé¨åãæ¸ãæãã
// æªå®æ: CEvalFuncã®ç½®ãæã applyCont = function applyCont(cont, value) { if (cont instanceof CHalt) { // <= function (value) { return value; } return value; } if (cont instanceof CEvalFunc) { return defuncEvaluate(cont.arg, cont.env, new CEvalArg(value, cont.env, cont.cont)); /* return defuncEvaluate(cont.arg, cont.env, function (arg) { if (value instanceof VFunc) { var newEnv = new Env(value.env); newEnv.put(value.param, arg); //debug(value.param + "=" + arg); return defuncEvaluate(value.body, newEnv, cont.cont); } else if (func instanceof VVal) { return applyCont(cont.cont, valueApply(value, arg)); } }); */ } if (cont instanceof CEvalArg) { // 対å¿: value => cont.func, cont.cont => cont.cont, arg => value if (value instanceof VFunc) { var newEnv = new Env(cont.func.env); newEnv.put(cont.func.param, value); //debug(cont.func.param + "=" + value); return defuncEvaluate(cont.func.body, newEnv, cont.cont); } else if (func instanceof VVal) { return applyCont(cont.cont, valueApply(cont.func, value)); } } };
ã©ãããCEvalArgã¯ä½¿ããªããã®ã¾ã§ä¿æãã¦ãããã ã
å®æç
è¤æ°ã®é¢æ°ããã¼ã¿ã«åããã¦å¯¾å¿é¢ä¿ã示ãã³ã¡ã³ããåã¾ã£ã¦ããã®ã§ã対å¿é¢ä¿ã®ã³ã¡ã³ããåãã¾ã¨ãããã®
// Cont CHalt = function CHalt() { }; CEvalFunc = function CEvalFunc(arg, env, cont) { this.arg = arg; this.env = env; this.cont = cont; }; CEvalArg = function CEvalArg(func, env, cont) { this.func = func; this.env = env; this.cont = cont; }; // evaluator: top level function runEvaluate(expr, env) { return defuncEvaluate(expr, env, new CHalt()); } // defunctionalized CPS evaluator defuncEvaluate = function defuncEvaluate(expr, env, cont) { if (expr instanceof EFunc) { return applyCont(cont, new VFunc(env, expr.param, expr.body)); } if (expr instanceof ECall) { return defuncEvaluate(expr.func, env, new CEvalFunc(expr.arg, env, cont)); } if (expr instanceof EVar) { return applyCont(cont, env.get(expr.name)); } if (expr instanceof EVal) { return applyCont(cont, new VVal(expr.value)); } }; // continuation runner applyCont = function applyCont(cont, value) { if (cont instanceof CHalt) { return value; } if (cont instanceof CEvalFunc) { return defuncEvaluate(cont.arg, cont.env, new CEvalArg(value, cont.env, cont.cont)); } if (cont instanceof CEvalArg) { if (value instanceof VFunc) { var newEnv = new Env(cont.func.env); newEnv.put(cont.func.param, value); //debug(cont.func.param + "=" + value); return defuncEvaluate(cont.func.body, newEnv, cont.cont); } else if (func instanceof VVal) { return applyCont(cont.cont, valueApply(cont.func, value)); } } };
æ«å°¾å帰é¢æ°ã®ã«ã¼ãå
ãã®æ¸ãæããã¹ãããã§ããã¨è¨å¤§ãªéã«ãªãã®ã§ãããæ¹ã ãã
åç´æ«å°¾å¼ã³åºãå帰é¢æ°ã®ã«ã¼ãåã¯ãreturnã®é¨åã以ä¸ã®ããã«ãã¦whileã«ã¼ãã«å ¥ããã°ããã
- å帰å¼ã³åºãã®return: å¼æ°ãä¸æ¸ããã¦continue;
- å帰å¼ã³åºãã§ãªãreturn: returnãã
function foo(arg1, arg2) { if (...) { ... return c; } if (...) { ... return foo(a, b); } } // â function foo(arg1, arg2) { while (true) { if (...) { ... return c; } if (...) { ... arg1 = a; arg2 = b; continue; } } }
注æç¹ã¯ãarg1ãarg2ãä¸æ¸ãããã¨ãå¯ä½ç¨ãèµ·ããç¹ãå¿ããªããã¨ããã¨ãã°å¼ã³åºãé¨ãreturn f(a, arg1.m)ã ããã¨ãã£ã¦ãarg1 = a; arg2 = arg1.m; ã¨ãããã¨ã¢ã¦ãã
ç¸äºæ«å°¾å帰å¼ã³åºãé¢æ°ã®ã«ã¼ãå
æ«å°¾å帰ã ãã©ãè¤æ°ã®é¢æ°ã交äºã«å¼ã³åã£ã¦ãå ´åã¯ã©ããããã
åºæ¬ã¯ãå¼æ°ã«å ¨é¨ã®é¢æ°å¼æ°ãã®ããããã«ãã©ã°ãç¨æãã¦æ¯ãåããå帰å¼ã³åºãã¯å¼ã³åºãé¢æ°ã®ãã©ã°ãã»ãããã¦å¼ã³åºããã¨ããæ³¥èãæ¹æ³ã«ãªããããã«å¿ç¨ã¨ãã¦ã¯
- å¤ããå¼ã°ããªãé¢æ°ã®å¼æ°ã¯ããã¼ã«ã«å¤æ°ã«ããã
- ããã¼ã«ããã«éå®ããããªããã·ã§ã¼ãã«ããããã
工夫ããã ããã°ãå ¥ãããããªãã®ã§ããã¾ãæ·±ããããªãã»ããããã
function f(a, b) {...} function g(a, c) {...} // â DO_F = 1; DO_G = 2; function h(flag, a1, b, a2, c) { switch (flag) { case DO_F: ... case DO_G: ... } }
ã¾ã¨ãã¨ã
CEvalFuncãCEvalArgã£ã¦ã®ã¯å¼ã³åºããã¨ã®å¦çã®ååã§ãããContinuation of Eval Funcã¨ãã ãã©ãå®éã®ã¨ããããã¨ã«è¡ãå¦çã®ååãä»ããã»ãããããããCEvalFunc=>CPushArgãCEvalArg=>CInvokeFuncã®ãããªæãã«ã
unboxå¦çã®valueApplyãCPSåããªãã£ãã®ã¯ã以éã§æ¸ãæããé¢æ°ãå¢ãã¦ãè¨è¿°éãå¢ãã¦é¢åãªããããã ãLazynessãå ¥ããå ´åããECallã§funcãåãåºãã¨ãã«å¼ã¶eagerã§ã¯å帰çã«evaluatorãé©ç¨ãããã¨ã«ãªã(ã¤ã¾ãevalåæ§ã«CPSé¢æ°ã«ããå¿ è¦ãåºã)ãã¾ãvalueApplyä¸ã«ãeagerãå ¥ãã®ã§valueApplyã®CPSæ¸ãæããå¿ é ã«ãªã£ã¦ãã¾ãã®ã§ããããã¨ããçµç·¯ã
ä¸é£ã®æ¸ãæãã¯æå¾ã¾ã§æ©æ¢°çã«å¯è½ãªã¯ããæ©è½çã«ã¯å帰ãã¼ã¸ã§ã³ããããã°ååã ãããã¨ãã°continuationã®ãããªãã®ã使ããããã«ããè¨èªç³»ã ã¨ãevalã®CPSåã¯ã»ã¼å¿ é ã®ãã®ã§ãããã¾ã§ãããªãé¢æ°ã®ãã¼ã¿åã¾ã§ããã¨ããããªã
ãªã½ã¼ã¹
- John C. Reynolds: Definitional Interpreters for Higher-Order Programming Languages
ã½ã¼ã¹å ¨ä½
ã³ã¼ãlang.js
// event handler function doRun() { var input = document.getElementById("input").value; var expr = eval(input); document.getElementById("output").innerHTML += "\n[expr]\n"; document.getElementById("output").innerHTML += expr.toString(); document.getElementById("output").innerHTML += "\n[evaluated]\n"; var result = evaluate(expr); document.getElementById("output").innerHTML += result.toString(); } function doClear() { document.getElementById("debug").innerHTML = ""; document.getElementById("output").innerHTML = ""; } // utilities debug = function debug(obj) { document.getElementById("debug").innerHTML += obj + "\n"; } curry = function curry(func, args) { var curried = function curried(arg) { var nargs = args.slice(0); nargs.push(arg); if (func.length == nargs.length) { var ret = func.apply(null, nargs); return ret; } else { return curry(func, nargs); } }; return curried; }; extend = function extend(ctor, obj) { if (! ctor.prototype) { ctor.prototype = {} } for (var prop in obj) { ctor.prototype[prop] = obj[prop]; } return ctor; } LObject = function LObject() { this.toString = function toString() { return this.toIndented(""); }; this.toIndented = function toIndented(indent) { var nested = indent + " "; var ret = this.constructor.name + " {\n"; for (var prop in this) { if (!this.hasOwnProperty(prop)) continue; ret += nested + prop + " = "; var val = this[prop]; if (typeof val == "object" && val["toIndented"]) { ret += val.toIndented(nested); } else { ret += val + "\n"; } } ret += indent + "}\n"; return ret; } }; // AST expr EFunc = function EFunc(param, body) { this.param = param; this.body = body; }; extend(EFunc, new LObject()); ECall = function ECall(func, arg) { this.func = func; this.arg = arg; }; extend(ECall, new LObject()); EVar = function EVar(name) { this.name = name; }; extend(EVar, new LObject()); EVal = function EVal(value) { this.value = value; } extend(EVal, new LObject()); // syntax sfunc = function sfunc(params, body) { if (params.length == 1) return new EFunc(params[0], body); var param = params[0]; var params = params.slice(1); return new EFunc(param, sfunc(params, body)); }; scall = function scall(func, args) { if (args.length == 1) return new ECall(func, args[0]); var arg = args[args.length-1]; var args = args.slice(0, args.length-1); return new ECall(scall(func, args), arg); }; slet = function slet(name, value, body) { return new ECall(new EFunc(name, body), value); }; svar = function svar(name) { return new EVar(name); }; sval = function sval(value) { return new EVal(value); }; sunit = function sunit() { return EVal(undefined); }; // Runtime Env = function Env(parent) { this.vars = {}; this.parent = parent; this.put = function put(name, value) { this.vars[name] = value; }; this.get = function get(name) { if (this.vars.hasOwnProperty(name)) { return this.vars[name]; } else { if (this.parent == null) { debug("var not found: " + name); return sunit(); } return this.parent.get(name); } }; }; // Value VVal = function VVal(value) { this.value = value; }; extend(VVal, new LObject()); VFunc = function VFunc(env, param, body) { this.env = env; this.param = param; this.body = body; }; extend(VFunc, new LObject()); // version 1: naive evaluator simpleEvaluate = function simpleEvaluate(expr, env) { if (expr instanceof EFunc) { return new VFunc(env, expr.param, expr.body); } if (expr instanceof ECall) { var func = simpleEvaluate(expr.func, env); // ToBe Lazy: func = eager(func) var arg = simpleEvaluate(expr.arg, env); // ToBe Lazy: arg = new VLazy(expr.arg, env); if (func instanceof VFunc) { var newEnv = new Env(func.env); newEnv.put(func.param, arg); //debug(func.param + "=" + arg); return simpleEvaluate(func.body, newEnv); } else if (func instanceof VVal) { return valueApply(func, arg); } } if (expr instanceof EVar) { return env.get(expr.name); } if (expr instanceof EVal) { return new VVal(expr.value); } }; // unboxing valueApply = function valueApply(func, arg) { var funcObj = func.value; if (typeof funcObj != "function") { return func; } if (funcObj.length > 1) { funcObj = curry(funcObj, []); } var funcArg = lambda2Js(arg); // ToBe Lazy: funcArg = function () { return lambda2Js(arg); } return new VVal(funcObj(funcArg)); }; lambda2Js = function lambda2Js(value) { // ToBe Lazy: value = eager(value); if (value instanceof VVal) { return value.value; } else if (value instanceof VFunc) { return function lambdaFunc(arg) { var func = new EFunc(value.param, value.body); var expr = new ECall(func, new EVal(arg)); var ret = runEvaluator(expr, value.env); return lambda2Js(ret); }; } }; // version 2: cps evaluator cpsEvaluate = function cpsEvaluate(expr, env, cont) { if (expr instanceof EFunc) { return cont(new VFunc(env, expr.param, expr.body)); } if (expr instanceof ECall) { return cpsEvaluate(expr.func, env, function (func) { func = reduce(func); return cpsEvaluate(expr.arg, env, function (arg) { if (func instanceof VFunc) { var newEnv = new Env(func.env); newEnv.put(func.param, arg); //debug(func.param + "=" + arg); return cpsEvaluate(func.body, newEnv, cont); } else if (func instanceof VVal) { return cont(valueApply(func, arg)); } }); }); } if (expr instanceof EVar) { return cont(env.get(expr.name)); } if (expr instanceof EVal) { return cont(new VVal(expr.value)); } }; // version 3: defunctionalized cps evaluator CHalt = function CHalt() { }; extend(CHalt, new LObject()); CEvalFunc = function CEvalFunc(arg, env, cont) { this.arg = arg; this.env = env; this.cont = cont; } extend(CEvalFunc, new LObject()); CEvalArg = function CEvalArg(func, env, cont) { this.func = func; this.env = env; this.cont = cont; } extend(CEvalArg, new LObject()); // cont is CXxxx object defuncEvaluate = function defuncEvaluate(expr, env, cont) { if (expr instanceof EFunc) { return applyCont(cont, new VFunc(env, expr.param, expr.body)); } if (expr instanceof ECall) { return defuncEvaluate(expr.func, env, new CEvalFunc(expr.arg, env, cont)); } if (expr instanceof EVar) { return applyCont(cont, env.get(expr.name)); } if (expr instanceof EVal) { return applyCont(cont, new VVal(expr.value)); } }; applyCont = function applyCont(cont, value) { if (cont instanceof CHalt) { return value; } if (cont instanceof CEvalFunc) { return defuncEvaluate(cont.arg, cont.env, new CEvalArg(value, cont.env, cont.cont)); } if (cont instanceof CEvalArg) { if (cont.func instanceof VFunc) { var newEnv = new Env(cont.func.env); newEnv.put(cont.func.param, value); //debug(cont.func.param + "=" + arg); return defuncEvaluate(cont.func.body, newEnv, cont.cont); } else if (cont.func instanceof VVal) { return applyCont(cont.cont, valueApply(cont.func, value)); } } }; // looped evaluator DO_EVAL = 0; DO_CONT = 1; loopedEvaluate = function loopedEvaluate(expr, env) { var cont1 = new CHalt(); var cont2 = null; var value = null; var invoke = DO_EVAL; //while (true) { for (var i = 0; i < 100; i += 1) { // evaluation switch (invoke) { case DO_EVAL: //debug(expr.constructor.name); if (expr instanceof EFunc) { value = new VFunc(env, expr.param, expr.body); cont2 = cont1; invoke = DO_CONT; //return applyCont(cont, new VFunc(env, expr.param, expr.body)); } else if (expr instanceof ECall) { cont1 = new CEvalFunc(expr.arg, env, cont1); expr = expr.func; invoke = DO_EVAL; //return defuncEvaluate(expr.func, env, new CEvalFunc(expr.arg, env, cont)); } else if (expr instanceof EVar) { value = env.get(expr.name); cont2 = cont1; invoke = DO_CONT; //return applyCont(cont, env.get(expr.name)); } else if (expr instanceof EVal) { value = new VVal(expr.value); cont2 = cont1; invoke = DO_CONT; //return applyCont(cont, new VVal(expr.value)); } else { debug("Expr ERROR"); return; } break; case DO_CONT: // continuation //debug(cont2.constructor.name); if (cont2 instanceof CHalt) { return value; } else if (cont2 instanceof CEvalFunc) { expr = cont2.arg; env = cont2.env; cont1 = new CEvalArg(value, cont2.env, cont2.cont); invoke = DO_EVAL; //return defuncEvaluate(cont.arg, cont.env, new CEvalArg(value, cont.env, cont.cont)); } else if (cont2 instanceof CEvalArg) { if (cont2.func instanceof VFunc) { var newEnv = new Env(cont2.func.env); newEnv.put(cont2.func.param, value); //debug(cont.func.param + "=" + arg); expr = cont2.func.body; env = newEnv; cont1 = cont2.cont; invoke = DO_EVAL; //return defuncEvaluate(cont.func.body, newEnv, cont.cont); } else if (cont2.func instanceof VVal) { value = valueApply(cont2.func, value); cont2 = cont2.cont; invoke = DO_CONT; //return applyCont(cont.cont, valueApply(cont.func, value)); } } else { debug("Cont ERROR"); return; } break; } } debug("ERROR"); } // evaluate runEvaluator = function runEvaluator(expr, env) { return loopedEvaluate(expr, env); //return defuncEvaluate(expr, env, new CHalt()); //return cpsEvaluate(expr, env, function (result) { return result; }); //return simpleEvaluate(expr, env); }; evaluate = function evaluate(expr) { return runEvaluator(expr, new Env(null)); // ToBe Lazy: return eager(runEvaluator(expr, new Env(null)); };
HTMLãã©ã¼ã ã¨ã
<html> <head> <script src="lang.js" type="application/javascript;version=1.7" /> </head> <body> <div> <form> <textarea id="input" cols="80" rows="10"> slet("k", sfunc(["x", "y"], svar("x")), scall(svar("k"), [sval(10), sval(20)])); </textarea> </form> <button onclick="doRun()">DO IT</button> <button onclick="doClear()">CLEAR LOG</button> </div> <pre id="debug"></pre> <pre id="output"></pre> </body> </html>