ä¹ ã ã«The n-Category Cafeãè¦ãããMike Stayã«ãã"The Continuation Passing Transform and the Yoneda Embedding"ãªãã¦è¨äºãããã¾ããã
ç±³ç°åãè¾¼ã¿ã¯åè«ã§ã¯ã馴æã¿ãç¶ç¶æ¸¡ãã¸ã®å¤æã¯ã³ã³ãã¥ã¼ã¿ã»ããã°ã©ãã³ã°ã§ã¯ã馴æã¿ã
ãã®2ã¤ã¯ãå®ã¯åããã®ãªãã ãããªãã§ã誰ããã®ãã¨ãè¨ããªããã ãããï¼
The Yoneda embedding is familiar in category theory. The continuation passing transform is familiar in computer programming.
They're the same thing! Why doesn't anyone ever say so?
Mike Stayã®ãã®è¨äºãé¢ç½ãã®ã ãã©ãC++ã¨Javaã¨åè«ã®è¨æ³ããããã説æããªãæ··ãã£ã¦åºã¦ãã¦ãã©ãããããã«ããï¼å°ãªãã¨ãåã«ã¯ï¼ãããã§ãJavaScriptã ãã使ã£ã¦èª¬æãã¦ã¿ã¾ããåè«ã¨ç±³ç°åãè¾¼ã¿ã®è©±ã¯ä»åã¯ãã¾ããããJavaScriptã«ãããç±³ç°åãè¾¼ã¿ï¼ç¥ããªãã¦ããã§ãï¼ï¼ï¼ã®å¯¾å¿ç©ãå®å ¨ã«æ¸ãä¸ãã¾ãã*1
å 容ï¼
- æªæ¥ã®JavaScript
- CPSï¼ç¶ç¶æ¸¡ãæ¹å¼ï¼ã¸ã®æ¸ãæã
- ãã£ã¨ç³»çµ±çã«CPSã¸ã¨å¤æãã
- ãã£ã¨ãã£ã¨ç³»çµ±çã«CPSã¸ã¨å¤æãã
- ã«ãªã¼åç»å ´
- CPSå¤æãè¡ãã¡ã¿ã¡ã¿é¢æ°
- ããã¦ãããã
æªæ¥ã®JavaScript
ãã¿ã«ããã¢ãã«ã®åãã«æ¸ãããããªçç±ã§ãä»åãã説æç¨æ¬ä¼¼ã³ã¼ãã¨ãã¦JavaScriptã使ãããã¨æãã¾ããããããããç¾ç¶ã®JavaScriptã§ã¯ãªãã¦ãè¿æªæ¥(?)ã®JavaScritã§ãããã®ä»æ§ã¯ï¼
- å¤æ°ãé¢æ°ã®å¼æ°ãé¢æ°ã®æ»ãå¤ã«å宣è¨ãä»ããããã
function max(x:Number, y:Number):Number
ã®ãããªæããããã¯JavaScript 2.0ã§ã¯ãµãã¼ããããã¯ãã - åãã©ã¡ã¼ã¿ã使ããç·ç§°åï¼åãã©ã¡ã¼ã¿ä»ãã®åï¼ãç·ç§°é¢æ°ï¼åãã©ã¡ã¼ã¿ä»ãã®é¢æ°ï¼ã使ããã
åãã©ã¡ã¼ã¿ã«ã¤ãã¦ã¡ãã£ã¨èª¬æãã¾ããä¾ãã°ãæååã®é å㯠Array<String> ã¨æ¸ããã¨ã«ãã¾ãããã®ã¨ããStringã¯åãã©ã¡ã¼ã¿ã§ããåã«Arrayã¨æ¸ããããã£ã¨æ£ç¢ºã«åãæå®ã§ãã¾ããã
Array<String> ã®å ´åã¯ããStringãåãã©ã¡ã¼ã¿ãã¨ã¯è¨ã£ã¦ããStringã¯æ¢ã«å®ã¾ã£ãåã§ãããåãã©ã¡ã¼ã¿ã®æ¬é ãçºæ®ãããã®ã¯ãããªãã ãããããªãããã¨ããåXãã使ã£ã¦ Array<X> ã®ãããªæ¸ãæ¹ãããã¨ãã§ãããã®ã¨ãã®Xã¯ãã¾ã ä¸å®ã®åã§ãããããã§ããã¨ããåãè¦ç´ ã¨ããé åãã¨ããæå³ãæã¡ã¾ãã
ãã ããXããã»ãã¨ã«Xã¨ããååã®ã¦ã¼ã¶ã¼å®ç¾©ã®åã§ããå¯è½æ§ãããã¾ãã®ã§ããæ¢ã«å®ã¾ã£ãåã§ã¯ãªãã¦ä¸å®ãªãã ãï¼ãã¨å¼·èª¿ããç®çã§ã<X> Array<X>ã¨ãç´åã«<X>ãä»ãããã¨ãããã¾ããããã¯ãã©ã ãå¼ Î»x.f(x) ã«ããã¦ãå¼f(x)å ã®å¤æ°xãã©ã ãè¨å·ã§æç¸ããã®ã¨åãã§ããä¸å®ã®åãã©ã¡ã¼ã¿ãæã¤åãç·ç§°åã¨å¼ã³ã¾ã*2ã
ç·ç§°é¢æ°ã¨ã¯ãä¸å®ã®åãã©ã¡ã¼ã¿ãå«ããããªé¢æ°ã§ã*3ãä¾ãã°ãArray2<X, Y> ã第1è¦ç´ ãXåã§ç¬¬2è¦ç´ ãYåã§ããé åã®åï¼åãã©ã¡ã¼ã¿ãï¼ã¤ãã¤ç·ç§°åã§ããï¼ã ã¨ãã¦ã
function makePair(first:X, second:Y):Array2<X, Y> { return [first, second]; }
ã¯ãåãã©ã¡ã¼ã¿XãYãå«ãç·ç§°é¢æ°ã§ããããã§ããXã¨Yããä¸å®ãªãã ãï¼ãã¨ç¤ºãããã«æ¬¡ã®æ¸ãæ¹ãæ¡ç¨ãããã¨ã«ãã¾ãããã
function<X, Y> makePair(first:X, second:Y):Array2<X, Y> { return [first, second]; }
function<X, Y>ã¯ãå¾ç¶ã®é¢æ°å®ç¾©å ã«ç»å ´ãããã¹ã¦ã®XãYãæç¸ãã¾ã -- æç¸ã®æ©æ§ã¯ãλ(x, y).(2x + y) ãªããã¨åãã§ãã
ç·ç§°é¢æ°ã«å®éã®å¼æ°ã渡ãã¦å¼ã³åºãã«ã¯ãä¸å®ã®åãã©ã¡ã¼ã¿ãå ·ä½åããªãã¦ã¯ãªãã¾ããããå¼ã³åºãã®ã¿ã¤ãã³ã°ã§ãå¼æ°ã®åã«å¿ãã¦å ·ä½åããããã¨èãã¦ãã ãããä¸ã®ä¾ã®makePairã§ã¯ãåãç¡è¦ãã1åã®é¢æ°ã«ããç·ç§°é¢æ°ãå®ç¾ããã¦ãã¾ãã¾ãããåãã©ã¡ã¼ã¿X, Yãå ·ä½åãããå½¢ã®é¢æ°ãã¤ããã¤ãã£ã¦ããã®å ¨ä½ï¼é¢æ°ã®æï¼ãç·ç§°é¢æ°ã ã¨æã£ã¦ãã ããã
CPSï¼ç¶ç¶æ¸¡ãæ¹å¼ï¼ã¸ã®æ¸ãæã
lengthããæååã®é·ããè¿ãé¢æ°ã ã¨ãã¾ããlength:String -> Integer ã®ãããªæ¸ãæ¹ã§ããé¢æ°lengthã¯ãStringåå¼æ°ãåããIntegeråã®å¤ãè¿ãããã¨ã表ãã¨ãã¾ããString -> Integer ã®é¨åãé¢æ°ã®ãããã¡ã¤ã«ã¨å¼ã³ã¾ãããã
ãã¦ã次ã®ã³ã¼ããèãã¾ãã
var n:Integer; n = length("Hello"); // ... // nã使ã£ã¦ä½ãããã // ...
ããã§ããnã使ã£ã¦ä½ããããã®é¨åããdoUsefulThing(n:Integer)ã¨ããé¢æ°ã«ã¾ã¨ããã°ã
var n:Integer; n = length("Hello"); doUsefulThing(n);
doUsefulThingã¯ãlengthã®çµæã使ã£ã¦ç¶ãã®å¦çãããã®ã§ãlengthã«å¯¾ããç¶ç¶å¦çé¢æ°ã¨å¼ã¶ãã¨ã«ãã¾ãããã¡ãããåãã£ã¦ããã°ã©ã å ¨ä½ã®å¦çãããã£ã¦ãªãã¨ããlengthã®å¾ã«ä½ããããããä¸æãªã®ã§ãããã°ã©ã å ¨ä½ã¨ã®é¢ä¿ã§ãlengthã«å¯¾ããç¶ç¶å¦çé¢æ°ãã決ã¾ãã¾ãã
ããã§ã次ã®é¢æ°ãä½ãã¾ããå宣è¨ããã¦ãªãcontã¯ãé¢æ°å¼æ°ã§ãï¼å宣è¨ã®è©³ç´°ã¯å¾è¿°ï¼ã
function doSomethingWithLength(cont, str:String):Void { var n:Integer = length(str); cont(n); }
ãã®doSomethingWithLengthã使ãã¨ãå ã®ã³ã¼ãã¯æ¬¡ã®ããã«æ¸ãæãããã¾ãã
doSomethingWithLength(doUsefulThing, "Hello");
ããã§ãdoSomethingWithLengthã®ç¬¬1å¼æ°ãç¶ç¶å¦çé¢æ°ã§ããã第2å¼æ°ãlengthã«æ¸¡ãæååã§ãããã¨ã«æ³¨ç®ãã¦ãã ãããç¶ç¶å¦çé¢æ°ãåã«ç¶ç¶ï¼continuationï¼ã¨ãå¼ã¶ã®ã§ã次ã®æ¸ãæããç¶ç¶æ¸¡ãæ¹å¼ï¼continuation passing style; CPSï¼ã¸ã®æ¸ãæãã¨å¼ã³ã¾ãã
- ãlengthã®å¼ã³åºãï¼ç¶ç¶å¦çã â ãdoSomethingWithLength(ç¶ç¶å¦çé¢æ°, lengthã¸ã®å¼æ°)ã
ãã£ã¨ç³»çµ±çã«CPSã¸ã¨å¤æãã
åç¯ã®doSomethingWithLengthã®å®ç¾©ã§ã第1å¼æ°ã®contï¼ç¶ç¶ã§ããï¼ã®å宣è¨ãããã¾ããããããããã§èãã¾ããããcontï¼ã«å ¥ãã¹ããã®ï¼ã¯ãé¢æ°ã§ãããlengthã®æ»ãå¤ã§ããæ´æ°ãå¼æ°ã«ã¨ããæ»ãå¤ã¯ããã¾ãããã¤ã¾ããcont:Integer -> Void ã§ããcontã®ãããã¡ã¤ã«ã§ãã Integer -> Void ã¯é¢æ°ã®åï¼é«éã®åï¼ãªã®ã§ããããã¡ã¤ã«ã®è¨æ³ããã®ã¾ã¾âåã®è¡¨ç¾âã¨ãã¦ãæ¡ç¨ãã¾ãããã
ããã¨ãdoSomethingWithLength(cont:Integer->Void, str:String):Void ã¨ãªãã¾ãã
ãããå¾ ã£ã¦ãã ãããcontã¯ãã¤ã§ãæ»ãå¤ãªãï¼æ»ãå¤Voidåï¼ã§ããã®ã§ããããï¼ ä¾ãã°ã次ã®ã³ã¼ããç¶ç¶æ¸¡ãæ¹å¼ã«å¤æãããã¨ãèãã¦ãã ããã
var n:Integer; n = length("Hello"); var x:Integer; x = n - 1;
å½ç¶ããã¯æ¬¡ã®ããã«ãããã§ãããã
var x:Integer; x = doSomethingWithLength(decrement, "Hello");
ã¨ãªãã¨ãé¢æ°doSomethingWithLengthã¯ããã®ç¬¬1å¼æ°ï¼ç¶ç¶ã§ãï¼ã®åã«å¿ãã¦æ»ãå¤ã®åãå¤åãããããªé¢æ°ã§ããåãå¤åãã -- ãããåãã©ã¡ã¼ã¿ã®åºçªã§ããããã³ãã
function<X> doSomethingWithLength(cont:Integer->X, str:String):X { return cont(length(str)); }
ããã¾ã§ã®è©±ã«åºã¦ããæªæ¥JavaScriptã®ã³ã¼ãã¯ãå宣è¨ãçãã¨ä»ã®JavaScriptã§ãåãã¾ãã試ãã¦ãã ããã
function doSomethingWithLength(cont, str) { return cont(length(str)); } function decrement(n) { return n -1; } var x; x = doSomethingWithLength(decrement, "Hello"); // x == 4
ãã£ã¨ãã£ã¨ç³»çµ±çã«CPSã¸ã¨å¤æãã
åç¯ã¾ã§ã®è©±ã¯ãlength:String -> Integer ã¨ããé¢æ°ãåºå®ãã¦ã®ãã¨ã§ããlength以å¤ã®é¢æ°ã«å¯¾ãã¦ããCPSã¸ã®å¤æï¼æ¸ãæãï¼ãã§ããããã«ãã¾ãããã
ãã®åã«è¨èã®æºåï¼ ããã ãã®ç¨èªæ³ã§ãããlengthã®ããã«ãç¶ç¶å¦çé¢æ°ã®ç´åã«å¼ã³åºãããï¼ããã¦ããã®å¤ã¯ç¶ç¶ã«æ¸¡ãããï¼é¢æ°ãå è¡é¢æ°ã¨å¼ã³ã¾ããããå è¡é¢æ°ãå¼ã³åºãããã®å¤ãç¶ç¶å¦çé¢æ°ã«æ¸¡ãä»äºãããdoSomethingWithLengthã®ãããªé¢æ°ï¼é«éé¢æ°ï¼ãç¶ç¶å¦çã¡ã¿é¢æ°ã¨å¼ã¶ãã¨ã«ãã¾ãã
ãã®è¨è使ãã§ãåç¯ã§ãã£ããã¨ãã¾ã¨ããã¨ï¼
- å è¡é¢æ°ã¯length:String -> Integer ã ããèããã
- lengthãå è¡é¢æ°ã¨ããç¶ç¶ï¼ç¶ç¶å¦çã®å®è¡é¢æ°ï¼ã¯ãå¼æ°ã®åãIntegerã§ãããæ»ãå¤åã¯ä»»æã
- 以ä¸ã®åæã§ãç·ç§°é¢æ°doSomethingWithLengthãæ¸ããã
ããããã¯ãä»»æã®å è¡é¢æ°hogeã«å¯¾ãã¦ãç¶ç¶å¦çã¡ã¿é¢æ°doSomethingWith_hogeãæ¸ãã®ãç®çã§ããå®ã¯ããã¯ç°¡åã§ãå è¡é¢æ°hogeã hoge:A -> B ã¨ãããããã¡ã¤ã«ãæã¤ãªãã
function<X> doSomethingWith_hoge(cont:B->X, arg:A):X { return cont(hoge(arg)); }
ã¨ããã ãã§ãã
ããã次ã®twice:String -> String ã¨ããé¢æ°ã«å¯¾ãã¦doSomethingWith_twiceãæ¸ããªããä¸ã®ããã«ãªãã¾ãã
function twice(str:String):String { return str + str; }
function<X> doSomethingWith_twice(cont:String->X, arg:String):X { return cont(twice(arg)); }
ä»ã®JavaScriptã«ç´ãã¦ã®ã³ã³ã½ã¼ã«å®è¡ä¾ã¯ï¼
js> doSomethingWith_twice(length, "Hello") 10
ã«ãªã¼åç»å ´
çè«çãªæ±ããåç´åããããã«ããã¹ã¦ã®é¢æ°ã1å¼æ°ã«ãã¾ãããããããã«ãªã¼åããã°ãããã§ãããç¶ç¶å¦çã¡ã¿é¢æ°ã§ããdoSomethingWith_lengthï¼è©±ã®æµãä¸ãLengthãã_lengthã«å¤æ´ï¼ãdoSomethingWith_twiceã2å¼æ°é¢æ°ã ã£ããã§ããããç¶ç¶ã ããåãåã1å¼æ°é¢æ°ã«æ¸ãç´ãã¾ãããã
ãªã¼ã«ããããããã¨ããã¾ããã
function<X> doSomethingWith_length(cont:Integer->X):String->X { return function(str:String) {return cont(length(str))}; }
function<X> doSomethingWith_twice(cont:String->X):String->X { return function(arg:String) {return cont(twice(arg))}; }
ä»ã®JavaScriptã§æ¸ããªãï¼
function doSomethingWith_length(cont) { return function(str) {return cont(length(str))}; } function doSomethingWith_twice(cont) { return function(arg) {return cont(twice(arg))}; }
ã¡ããã¨åãã¾ããã
js> doSomethingWith_length(decrement)("Hello") 4 js> doSomethingWith_twice(length)("Hello") 10
CPSå¤æãè¡ãã¡ã¿ã¡ã¿é¢æ°
ãã¦ãä»ã¾ã§ã®è©±ã§åãã¡ã¯ãä¸ããããå è¡é¢æ°ï¼ä¾ãã°lengthãtwiceï¼ãããããã«å¯¾å¿ããç¶ç¶å¦çã¡ã¿é¢æ°ãæ¸ãä¸ãã¾ãããå è¡é¢æ°lengthã«å¯¾ãã¦ã¡ã¿é¢æ°doSomethingWith_lengthãå è¡é¢æ°twiceã«å¯¾ãã¦ã¯ã¡ã¿é¢æ°doSomethingWith_twiceãã¨ããå ·åã«ã
人æã§CPSã¸ã®å¤æãã§ãããªãããã®æé ãé¢æ°ã¨ãã¦æ¸ãä¸ããã¨ãã§ããã¯ãã§ãããã¤ã¾ããå è¡é¢æ°ãå¼æ°ã«ã¨ã£ã¦å¯¾å¿ããç¶ç¶å¦çã¡ã¿é¢æ°ãä½ãåºãé¢æ°ï¼å¤ãã¡ã¿é¢æ°ã§ãããã¡ã¿ã¡ã¿é¢æ°ã§ããï¼ãæ¸ãã¦ã¿ã¾ãã
ãã®é¢æ°ã cpsTransformã ã¨ããã¨ãcpsTransformã®ãããã¡ã¤ã«ã¯
- (A->B) -> ((B->X)->(A->X))
ã§ãããããã¾ããï¼ (A->B)ãå è¡é¢æ°ã®åã§ããå è¡é¢æ°ãlengthãªãã(String->Integer)ã¨å ·ä½åããã¾ããç¶ç¶å¦çã¡ã¿é¢æ°ã¯ãåã(B->X)ã§ããç¶ç¶ãåãåã£ã¦ãåã(A->X)ã§ããé¢æ°ãè¿ãã¾ãï¼ã«ãªã¼åãã¦ãã¾ããï¼ãã¤ã¾ããç¶ç¶å¦çã¡ã¿é¢æ°ã®å㯠((B->X)->(A->X) ã§ããããã£ã¦ãCPSå¤æãè¡ãã¡ã¿ã¡ã¿é¢æ°ã®ãããã¡ã¤ã«ã¯ (A->B) -> ((B->X)->(A->X)) ã£ã¦ããã§ãã念ã®ããã¾ã¨ããã°ï¼
- å è¡é¢æ° : (A->B)
- ç¶ç¶ : (B->X)
- ç¶ç¶å¦çã¡ã¿é¢æ°ã®æ»ãå¤ : (A->X)
- ç¶ç¶å¦çã¡ã¿é¢æ° : ((B->X)->(A->X))
- CPSå¤æã¡ã¿ã¡ã¿é¢æ° : (A->B) -> ((B->X)->(A->X))
function<A, B, X> cpsTransform(prec:A->B):(B->X)->(A->X) { return function(cont:B->X):A->X { return function(arg:A):X {return cont(prec(arg))}; }; }
ä»ã®JavaScriptãªãã°æ¬¡ã§ãã
function cpsTransform(prec) { return function(cont) { return function(arg) {return cont(prec(arg))}; }; }
js> var cps_length = cpsTransform(length) js> cps_length(decrement)("Hello") 4 js> var cps_twice = cpsTransform(twice) js> cps_twice(length)("Hello") 10
ããã¦ãããã
ããã¾ã§æ¥ãã°ãcpsTransformããéååã®é©å½ãªé¨ååCãããé¢æå[Cop, Set]ã¸ã®é¢æã表ç¾ãã¦ãããã¨ã確èªããã®ã¯å®¹æã§ãï¼å®ç¾©ãç¥ã£ã¦ããã°ï¼ãåã¯Cã®å¯¾è±¡ãé¢æ°ã¯Cã®å°ãã¡ã¿é¢æ°ã¯é¢æåã®å¯¾è±¡ãã¡ã¿ã¡ã¿é¢æ°ãé¢æåã®å°ã«ãªãã¾ãã
ããã¦ãcpsTransformã®å®ç¾©ãã¦ãããã«è¿½ããããã°ããããç±³ç°åãè¾¼ã¿ã¨ã¾ã£ããåãã§ãããã¨ãå¤æããããã§ãããããã¯ã¾ãã®æ©ä¼ã«ã
æå¾ã«ä¸è¨æ³¨æï¼ ãã®è¨äºã§ã¯ãã«ã¬ã³ãç¶ç¶ããã£ããã£ããæ©è½ã«ã¯ã¾ã£ãã触ãã¾ããã§ãããç¾å®ã®ããã°ã©ãã³ã°è¨èªã§ã¯ããã®ãã£ããã£æ©è½ãéè¦ã«ãªãã¾ãã
åèï¼
- ã©ã ãè¨ç® âJavaScriptで学ぶ・プログラマのためのラムダ計算 - 檜山正幸のキマイラ飼育記 (はてなBlog)
- ã©ã ãè¨ç® â絵を描いて学ぶ・プログラマのためのラムダ計算 - 檜山正幸のキマイラ飼育記 (はてなBlog)
- åè« âはじめての圏論 その第1歩:しりとりの圏 - 檜山正幸のキマイラ飼育記 (はてなBlog)
- ç±³ç°ã®è£é¡ â圏論番外:米田の補題に向けてのオシャベリ - 檜山正幸のキマイラ飼育記 (はてなBlog)
- ç±³ç°ã®è£é¡ â圏論番外:米田の補題の周辺から - 檜山正幸のキマイラ飼育記 (はてなBlog)
- ç±³ç°åã込㿠â圏論番外:米田埋め込み (前編) - 檜山正幸のキマイラ飼育記 (はてなBlog)
- ç¶ç¶ â非同期呼び出しって継続っぽい -- 時間軸を加工する話 - 檜山正幸のキマイラ飼育記 (はてなBlog)
- ã«ãªã¼å âJavaScriptでカリー化 - 檜山正幸のキマイラ飼育記 (はてなBlog)
- ã«ãªã¼å âもっとかしこいカリー化 - 檜山正幸のキマイラ飼育記 (はてなBlog)
- ãã«ã«ãå âなぜ「光が影を作ること」と「主張の一部を再主張すること」が関係するのか;あるいは、デカルト圏入門 - 檜山正幸のキマイラ飼育記 (はてなBlog)
- ãã«ã«ãéå â圏論的指数の周辺:ラムダ計算、デカルト閉圏、ノイマン型コンピュータ - 檜山正幸のキマイラ飼育記 (はてなBlog)
- ãã«ã«ãéå âモノイド圏、豊饒圏、閉圏と内部ホム - 檜山正幸のキマイラ飼育記 (はてなBlog)
*1:åã¯æ æ¨çåºèº«ãªã®ã§ãã¿ã¤ãã«ã®åé¢æ±å¼ãæ¬ç©ã§ãããã¨ã«èªä¿¡ãããã¾ãã
*2:ä¸å®ã®åãã©ã¡ã¼ã¿ãæããªãå ´åã§ããä¸å®ã®åãã©ã¡ã¼ã¿ã0åã®ç·ç§°åã¨èãããã¨ãããã¾ããå®æ°ãå¼æ°ãªãé¢æ°ã¨ã¿ãªãã®ã¨åãã§ãã
*3:ãç·ç§°é¢æ°ãã¨ããè¨èãå¥ãªæå³ã§ä½¿ããã¨ãããã¾ããããã®è¨äºå ã§ã¯å ·ä½åããã¦ãªãåãã©ã¡ã¼ã¿ãå«ãé¢æ°ã§ãã