ä½è¨ã£ã¦ããã ã¨ããæãã§ããããããæãã§ãã
(defun find-user (username) (if (string= username "windymelt") (return-left :windymelt) (return-right "User Not Found"))) (defun check-passwd (user passwd) (if (and (eq user :windymelt) (string= passwd "123456")) (return-left t) (return-right "Password authentication failed"))) (defun get-user-age (user) (if (eq user :windymelt) (return-left 26) (return-right "User Age Not Found"))) (defun ensure-adult (age) (if (< age 20) (return-right "Should be adult") (return-left t))) (defun purchase-beer (user) (declare (ignorable user)) (print "beer!")) (defun beer-controller (user-name passwd) (for-comprehension (user <- (find-user user-name)) (passwd-guard <- (check-passwd user passwd)) (age <- (get-user-age user)) (adult-guard <- (ensure-adult age)) (_ <- (purchase-beer user))))
ããããã®ãæ¸ãã¾ããMaybeããå®è£ ãã¦ãªãã®ã§for-comprehensionå®è£ ããã¨ããã®ã¯ã¡ãã£ã¨çãããããã
å®éã«åä½ãããã¨ããããæãã§ï¼ã¡ããã¨Maybeãªæåã«ãªã£ã¦ãã¾ãã
CL-USER> (beer-controller "windymelt" "") NIL "Password authentication failed" CL-USER> (beer-controller "windymel" "") NIL "User Not Found" CL-USER> (beer-controller "windymelt" "123456") "beer!" "beer!" NIL
ããã¢ã³ãªã®ã§ã¡ããã¨ã¯ãã¦ããªãã®ã§ããï¼ãã詳ããä»çµã¿ã解説ãã¦ã¿ã¾ãã
for comprehension is ä½
Scalaã®é¢ç½æ©è½ã ã¨æã£ã¦ããã©Haskellã¨ãã«ãããã¯ãã
ã¢ããã¨ãããã£ã¡ãããããã®ã«ä¾¿å©ãªãã¤ã§ããï¼è©³ããã¯ããã§ã¯èª¬æãã¾ããã
å¤å¤
ã³ã¼ãä¸ã§Maybeãã©ããã£ã¦è¡¨ç¾ãããã¨ããã¨ï¼ã¾ãç´ æ´ã«ãã£ãããªã¹ãã¨ãæ§é ä½ã使ãã¨æããã§ããï¼ä»åã¯ç·´ç¿ãå ¼ãã¦å¤å¤ã使ããã¨ã«ãã¾ããã
å¤å¤ is ä½
Golangã®ãããã§æµ¸éãã¦ãããããªæ°ãããæ¦å¿µã§ããï¼ããããã«ä¸åº¦ã«è¤æ°ã®å¤ãè¿ããã¨ããæ©è½ã§ããã¿ãã«è¿ãã®ã¨ã¯å¥ç©ã§ãã詳ããã¯âè¦ã¦ãã ããã
Common Lispã§ã¯values
ãå¼ã¶ãã¨ã§å¤å¤ãè¿ããã¨ãã§ãï¼multiple-value-bind
ãªã©ã使ã£ã¦å¤å¤ãåãåããã¨ãã§ãã¾ãã
ãã¾ãããã¨ã¬ã¸ã¹ã¿ã«å¤ãå
¨é¨è¼ããããã®ã§é«éã§ã¨ã³ã«ãªãã¯ãã§ãããªã¹ãã¯ä½ã£ãç¬éã«ã¡ã¢ãªç¢ºä¿ãçºçããããã¾ããCommon Lispã§é度ãéè¦ããå ´åï¼ããã«ãªã¹ãä½æã«ããã¡ã¢ãªç¢ºä¿ï¼ã³ã³ã·ã³ã°consingã¨å¼ã°ããããã¾ãï¼ãæ¸ããããåè² ã§ãï¼ãã¶ãï¼ã軽éã«åãMaybeã¢ããï¼ã¡ãã£ã¨æ¬²ããã£ãã®ã§ä½ã£ã¦ãã¾ã£ãã¨ããããã§ãã
å¤å¤ã§Maybeã¢ãã
Maybeã¢ãããå¤å¤ã§è¡¨ç¾ãã¾ããããæåã«è¿ãããå¤ãleftï¼2çªç®ã®å¤ãrightã¨ãã¾ããããããã¯è¦ãç®ãããã¦èªç¶ãªå®ç¾©ã§ãã
leftã¨rightã§å
ãã å¤ãè¿ããreturn-left
ã¨return-right
ã¯ï¼ä»¥ä¸ã®ããã«æ¸ãã¾ãããä»æ°ä»ãããã©ãã¯ããããªãã¦è¯ãã£ãæ°ãããã
(defmacro return-left (x) `(values ,x nil)) (defmacro return-right (x) `(values nil ,x))
åããã¦ã¿ã¾ãã
CL-USER> (return-left 12) 12 NIL CL-USER> (return-right 34) NIL 34
é¢æ°ãè¤æ°ã®å¤ãè¿ãã¦ãã¾ããããããå¤å¤ã§ãã
Maybeå¤å¤ãé£éããã
ã¢ããããã£ãããã§ã¤ã³ããããï¼ãã¨ããï¼
flatmap
ãå®è£
ãã¾ãããã
(defmacro flatmap (f xform) `(multiple-value-bind (left right) ,xform ;; left projection (if left ,f (return-right right))))
flatmap
ã¯ç¬¬2å¼æ°ã®ãã©ã¼ã ããå¤å¤ãåãåãï¼left
ããããªãã°f
ãå¼ã³åºãã¾ãããããªããã°ï¼right
ãè¿ãã¦ãã¾ãã¾ãã
ã¨ããã§ããæ¬å½ã«flatmapãªã®ã§ãããããä¸å®ã«ãªã£ã¦ãã¾ãããããã¢ã³ãªã®ã§å¯ã¨ãã¾ãã
map
ãä½ãã¾ãããããã£ã¦ããã¨ã¯ã»ã¼ä¸ç·ã§ããè¿ãå¤ãMaybeã«ãªããã«å¤ããã®ã¾ã¾å¾ãããã
(defmacro maybe-map (f xform) `(multiple-value-bind (left right) ,xform ;; left projection (if left ,f right)))
æ¬å½ã«ããã§ããã®ãä¸å®ã§ãã
for-comprehension
ãã¦ï¼scalaã®for-comprehensionã¯flatmapã¨filterã¨mapã¨ã«å解ã§ãããã¨ãç¥ããã¦ãã¾ããã¨ããããã®ã·ã¥ã¬ã¼ã·ã³ã¿ãã¯ã¹ã§ãããããã£ã¦ï¼ä»åã¯for-comprehensionãªå¼ãåãåã£ã¦ï¼ããããã£ã¡ãããã£ã¡ãããã¦flatmapã¨mapã¨ã«çµã¿å¤ãã¦ãã¾ãã°ããããã§ããfilterã¯é¢åã ã£ãã®ã§ãã¼ãã¾ããã
ä½æ¦
ç´ä½æ²æãã£ãã®ã§ããããããä½æ¦ã§å®è£ ãã¦ãã¾ããfor-comprehensionèªä½ã¯è¨ç®ãè¡ããï¼ãã¯ãã«ããå¼å¤å½¢ã«çããã¹ã¿ã¤ã«ã§ãã
;; åè (for-comprehension (a <- (f)) ; 1æ®µç® (b <- (g a)) ; 2æ®µç® (c <- (h a b))) ; 3段ç®
- ã¾ãæãä¸ã®æ®µããï¼
flatmap
ã«ãããã§å¼ã¶ã³ã¼ããçæããããããcode
ã¨ããã - 次ã®æ®µã«ç§»ãï¼ã¾ã
flatmap
ã«ãããã§å¼ã¶ã³ã¼ããçæããããã®ã¨ãflatmap
ã®å¼æ°ã¨ãã¦code
ã渡ããçæãããã³ã¼ãã§code
ãä¸æ¸ãããã - 段ãããéã
code
ãæ´æ°ãã¤ã¤flatmap
ã§ãããã§ããã
ã¾è¦ããã«flatmapã使ãå½¢å¼ã«å¤æãã¦ããã ãã§ãããã ãã¯ããã¨ããã£ã¦ãã¦ï¼a,b,cã¨ãã£ãå¤æ°ã®ã¹ã³ã¼ããhandler-bind
ã®å¤å´ã«åã°ãªãï¼å½ç¶ï¼ã®ã§ãã£ããä¸å±¤ã«ã·ã³ãã«ãç¨æãï¼ãã®plistã«ä¿ç®¡ããããã¦ãã¾ãã
説æããã®ãé¢åã«ãªã£ã¦ããã®ã§ã³ã¼ããåºãã¾ãã
(ql:quickload '(:iterate :alexandria)) (use-package :iterate) (use-package :alexandria) ;; 段ã®å¤ãããã¾ã£ãå¤æ°ã¯ï¼ã¾ã¨ãã¦1ã¤ã®ã·ã³ãã«ã®ããããã£ã«æ ¼ç´ããããã«ãã¦ããã ;; ãã®ãã段ã®å¤æ°ãåç §ããç®æãç½®æãã (defun sanitize-variables-for-for-comprehension (form var) (mapcar #'(lambda (x) (etypecase x (list (sanitize-variables-for-for-comprehension x var)) (t (if (eq x var) `(get *for-comprehension-variables* ,(make-keyword (concatenate 'string "%" (string var)))) ;; ååããã®ã¾ã¾ä½¿ãã¨è¡çªããã®ã§ã·ã³ãã«ã«%ãã¤ãã x)))) form)) (defmacro for-comprehension (&rest clauses) (iter (for c :in clauses) (with vars) ;; ããã«ãã©ãããå¤æ°ã«ä»£å ¥ãããï¼ãããåããããããã¨ã§ä½¿ã (for code :first ;; codeã®åæå¤ã決ãã (ecase (second c) ;; '<-' or '=' (<- (progn (push (first c) vars) ;; å¤æ°åã·ã³ãã«ãéé¿ãã `(multiple-value-bind (left right) (flatmap ,(third c) t) ;; flatmapãããï¼1段ç®ãªã®ã§ç¹ã«ãªã«ã渡ããªãã¦ãããããã¼ã§tã渡ãå½¢å¼ã«ãã (setf (get *for-comprehension-variables* ,(make-keyword (concatenate 'string "%" (string (first c))))) (if left left right)) ;; å¾ãããå¤ããã大åãªå¤æ°ã«éé¿ããã (values left right))))) ;; éé¿ãããå¾ã¯ãªã«ãã¨ããªãå ã®å¤å¤ãè¿ã :then ;; 2段ç®ä»¥éã¯ãããå¼ã°ãã (if (eq (first c) 'yield) ;; yieldã ã£ããmapå¼ã¶ `(maybe-map ,(second c) ,code) (ecase (second c) (<- (progn (push (first c) vars) `(multiple-value-bind (left right) (flatmap ,(third c) ,code) ;; å段ã§çæãããcodeã«å¯¾ãã¦flatmapããã¨ããå½¢å¼ã«å¤æãã (setf (get *for-comprehension-variables* ,(make-keyword (concatenate 'string "%" (string (first c))))) (if left left right)) (values left right))))))) (finally (return `(progn (let ((*for-comprehension-variables* nil)) ;; ããã«flatmapããçµæãè©°ãè¾¼ãã§ããã¦ï¼å¤æ°åã使ã£ã¦åç §ã§ããããã«ãã¦ãã ,(progn (dolist (v vars) (setf code (sanitize-variables-for-for-comprehension code v))) ;; å¤æ°åã§åç §ãã¦ããç®æã¯âã®å¤æ°ããã®èªã¿è¾¼ã¿ã«ç½®æãã code)))))))
ããã§Maybeã¢ãããã©ããå®è£ ã§ãã¾ããããflatmapã®å®è£ ãããã£ããOptionã¨ãã«å¯¾å¿ãããï¼right projectionã«ã§ããã¨æãã¾ãã
ãªãã§right projectionãããªãã¦left projectionãªã®
ããã©ã§ã¯ï¼æ®éã®é¢æ°ãå¤å¤ãåãåãã¨1ã¤ç®ã®å¤ãåãåãããã«ãªã£ã¦ããã®ã§ï¼ããã¨ã®æ´åæ§ãåªå ãã¾ããã
CL-USER> (identity (values 1 2 3 4 5)) 1
ãã¶ã欲ããå¤ã¯1çªç®ã«ããã®ãæ®éã ããï¼ã¨ããããã§ãã
ãªãã§ãªã¹ã使ããªãã£ãã®
ãªã¹ãã§ããã¨æãã¾ãã
ã¾ã¨ã
ãªãã¨ãªã便å©ãããªã®ãã§ããã
ãã§ãã