
é¢æ°åããã°ã©ãã³ã°ïŒFPïŒã䜿çšãããšãã³ãŒãäœæã®ã¢ãããŒããæ¹åã§ããŸãã ããããAFãç¿åŸããã®ã¯ç°¡åã§ã¯ãããŸããã å€ãã®èšäºãããã¥ã¢ã«ã§ã¯ãMonadsãApplicativeãªã©ã®è©³çŽ°ã«æ³šæãæã£ãŠããŸãããã匷åãªFPãã¯ããã¯ãæ¥åžžçã«äœ¿çšããã®ã«åœ¹ç«ã€å®äŸãšããŠã®å®äŸãæããŠããŸããã ãã®çç¥ãä¿®æ£ããããšã«ããŸããã
匷調ãããã®ã¯ããã®èšäºã§ã¯ãæ©èœX ãå¿
èŠãªçç±ã§ã¯ãªããæ©èœX ãå¿
èŠãªçç±ã«çŠç¹ãåœãŠãŠããŸã ã
é¢æ°åããã°ã©ãã³ã°
FPã¯ãäžé£ã®æ©èœãåçŽã«çµã¿åãããããã°ã©ã äœæã¹ã¿ã€ã«ã§ãã ç¹ã«ãFPã«ã¯ãã»ãŒãã¹ãŠã®é¢æ°ãé¢æ°ã§ã©ããããããšãå«ãŸããŸãã ïŒfunc1.func2.func3ïŒã®ãããªçµæãŸãã¯func1ïŒfunc2ïŒfunc3ïŒïŒïŒïŒã®ãããªçµæãåŸãã«ã¯ãå€ãã®å°ããªåå©çšå¯èœãªé¢æ°ãèšè¿°ãããããã次ã
ã«åŒã³åºãå¿
èŠããããŸãã
ããããå®éã«ãã®ã¹ã¿ã€ã«ã§ããã°ã©ã ãäœæããã«ã¯ãé¢æ°ãç¹å®ã®ã«ãŒã«ã«åŸã£ãŠããã€ãã®åé¡ã解決ããå¿
èŠããããŸãã
AFã®åé¡
äžé£ã®æ©èœãçµã¿åãããããšã§ãã¹ãŠãå®è¡ã§ããå Žåã...
- if-elseæ¡ä»¶ãåŠçããæ¹æ³ã¯ïŒ ïŒãã³ãïŒã¢ããã®ã©ã¡ããïŒ
- nulläŸå€ãåŠçããæ¹æ³ã¯ïŒ ïŒãã³ãïŒãã¶ãã¢ããïŒ
- é¢æ°ãæ¬åœã«åå©çšå¯èœã§ãããã©ãã§ã䜿çšã§ããããšã確èªããæ¹æ³ã¯ïŒ ïŒãã³ãïŒçŽç²ãªæ©èœãåç
§ééæ§ïŒ
- ç§ãã¡ããé¢æ°ã«æž¡ãããããŒã¿ãå€æŽããããä»ã®å Žæã§äœ¿çšã§ããããšã確èªããæ¹æ³ã¯ïŒ ïŒãã³ãïŒçŽç²ãªé¢æ°ãäžå€æ§ïŒ
- é¢æ°ãè€æ°ã®å€ãåããããã§ãŒã³ã«çµåãããå ŽåïŒãã§ãŒã³ïŒãäžåºŠã«1ã€ã®é¢æ°ããæž¡ãããšãã§ããªãå Žåããã®é¢æ°ããã§ãŒã³ã®äžéšã«ããã«ã¯ã©ãããã°ããã§ããïŒ ïŒãã³ãïŒã«ãªãŒåãšé«æ¬¡é¢æ°ïŒ
- ãã®ä»<質åãããã«è¿œå >ã
FPãœãªã¥ãŒã·ã§ã³
ãããã®ãã¹ãŠã®åé¡ã解決ããããã«ãHaskellã®ãããªå®å
šã«æ©èœããèšèªã¯ãã¢ããããã¡ã³ã¯ã¿ãŒãªã©ã®ããŸããŸãªããŒã«ãšæ°åŠæŠå¿µãæäŸããŸããJavaScriptã¯ãè±å¯ãªããŒã«ãæäŸããŸãããã幞ããªããšã«ãååãªFPããããã£ã®ã»ããããããŸããã©ã€ãã©ãªãäœæã§ããŸãã
ãã¡ã³ã¿ãžãŒã©ã³ããšFPã©ã€ãã©ãªã®ä»æ§
ã©ã€ãã©ãªããã¡ã³ã¯ã¿ãŒãã¢ãããªã©ã®æ©èœãæäŸããå ŽåãæäŸãããæ©èœãHaskellãªã©ã®èšèªãšåãã«ãªãããã«ãç¹å®ã®ä»æ§ãæºããé¢æ°/ã¯ã©ã¹ãå®è£
ããå¿
èŠããããŸãã
é¡èãªäŸã¯ãåJSé¢æ°/ã¯ã©ã¹ã®åäœæ¹æ³ã説æããFantasy Landã®ä»æ§ã§ãã

ãã®å³ã¯ããã¹ãŠã®ä»æ§ãšãã®äŸåé¢ä¿ã瀺ããŠããŸãã ä»æ§ã¯åºæ¬çã«æ³åŸã§ãããJavaã€ã³ã¿ãŒãã§ãŒã¹ã«äŒŒãŠããŸãã JSã®èŠ³ç¹ããã¯ãä»æ§ã¯ãä»æ§ã«åŸã£ãŠããã€ãã®ã¡ãœããïŒ mapãofãchainãªã©ïŒãå®è£
ããã¯ã©ã¹ãŸãã¯ã³ã³ã¹ãã©ã¯ã¿ãŒé¢æ°ãšèŠãªãããšãã§ããŸãã
äŸïŒ
JSã¯ã©ã¹ã¯ãmapã¡ãœãããå®è£
ããå Žåã¯Functorã§ãã ãŸãããã®ã¡ãœããã¯ä»æ§ã§èŠå®ãããŠãããšããã«æ©èœããå¿
èŠããããŸãïŒèª¬æã¯ç°¡ç¥åãããå®éã«ã¯ããå€ãã®ã«ãŒã«ããããŸãïŒã
JSã¯ã©ã¹ã¯ãä»æ§ã«åŸã£ãŠmapããã³apé¢æ°ãå®è£
ããå ŽåãApplyïŒApply FunctorïŒãã¡ã³ã¯ã¿ãŒã§ãã
JSã¯ã©ã¹ã¯ãFunctorãApplyãApplicativeãChainãããã³Monadèªäœã®èŠä»¶ãïŒäŸåé¢ä¿ãã§ãŒã³ã«åŸã£ãŠïŒå®è£
ããå Žåãã¢ããïŒMonad FunctorïŒã§ãã
泚ïŒäŸåé¢ä¿ã¯ç¶æ¿ã®ããã«èŠããŸãããå¿
é ã§ã¯ãããŸããã ããšãã°ãã¢ããã¯ãïŒæ®ãã«å ããŠïŒApplicativeãšChainã®äž¡æ¹ã®ä»æ§ãå®è£
ããŸãã
Fantasy Landæºæ ã®ã©ã€ãã©ãª
FLä»æ§ãå®è£
ããã©ã€ãã©ãªãããã€ããããŸãã ããšãã°ã monet.js ã ã»ãšãã©æ©èœããªã ã folktalejs ã ramda-fantasy ïŒ Ramdaã«åºã¥ãïŒã immutable-ext ïŒImmutableJSã«åºã¥ãïŒã Flutureãªã©ã§ãã
ã©ã®ã©ã€ãã©ãªã䜿çšããå¿
èŠããããŸããïŒ
lodash-fpãramdajsã®ãããªã©ã€ãã©ãªã§ã¯ãFPã®ã¹ã¿ã€ã«ã§ã®ã¿èšè¿°ã§ããŸãã ããããå®éã®åé¡ã解決ã§ããã¢ããããã¡ã³ã¯ã¿ãŒãFoldableãªã©ã®äž»èŠãªæ°åŠæŠå¿µã䜿çšããããã®æ©èœã¯æäŸããŠããŸããã
ãã®ãããFLä»æ§ã䜿çšããã©ã€ãã©ãªã®ãããããéžæããããšããå§ãããŸãïŒ monet.js ã ã»ãšãã©æ©èœããªã ã folktalejs ã ramda-fantasy ïŒ Ramdaã«åºã¥ãïŒã immutable-ext ïŒImmutableJSã«åºã¥ãïŒã Fltureãªã©
æ³šïŒ ramdajsãšramda -fantasyã䜿çšããŸãã
ããã§ãåºæ¬ã®ã¢ã€ãã¢ãåŸãã®ã§ãå®éã®äŸã«ç§»ããFPã®ããŸããŸãªå¯èœæ§ãšãã¯ããã¯ãç 究ããŸãããã
äŸ1. Nullãã§ãã¯ã®äœ¿çš
ãã®ã»ã¯ã·ã§ã³ã§ã¯ããã¡ã³ã¯ã¿ãŒãã¢ãããå€åã¢ãããã«ãªãŒã«ã€ããŠèª¬æããŸã
ã¢ããªã±ãŒã·ã§ã³ïŒ åªå
èšèªã®ãŠãŒã¶ãŒèšå®ã«å¿ããŠãç°ãªãéå§ããŒãžã衚瀺ããŸã ã ãŠãŒã¶ãŒïŒjoeUserïŒã®åªå
èšèªïŒã¹ãã€ã³èªïŒã®URL ïŒindexURLïŒã®ãªã¹ããã察å¿ããURLãè¿ãgetUrlForUserãèšè¿°ããå¿
èŠããããŸãã

åé¡ïŒèšèªã¯nullã«ã§ããŸããã ãŸãããŠãŒã¶ãŒãnullã«ããããšã¯ã§ããŸããïŒãã°ã€ã³ããŠããŸããïŒã èšèªãindexURLã®ãªã¹ãã«ãªãå ŽåããããŸãã ãããã£ãŠãå€æ°ã®ãã«ãŸãã¯æªå®çŸ©ã®äžè©±ãããå¿
èŠããããŸãã
//TODO const getUrlForUser = (user) => { //todo } // let joeUser = { name: 'joe', email: 'joe@example.com', prefs: { languages: { primary: 'sp', secondary: 'en' } } }; // indexURL' let indexURLs = { 'en': 'http://mysite.com/en', // 'sp': 'http://mysite.com/sp', // 'jp': 'http://mysite.com/jp' // } //apply url to window.location const showIndexPage = (url) => { window.location = url };
ãœãªã¥ãŒã·ã§ã³ïŒå¿
é ãšæ©èœïŒïŒ
FPããŒãžã§ã³ãç解ããã®ãé£ãããšæãããå Žåã§ãå¿é
ããªãã§ãã ããã ããã«ãã®èšäºã§ã¯ãã¹ãããããšã«åæããŸãã
// : // if-else null; indexURL'; «» URL' const getUrlForUser = (user) => { if (user == null) { // return indexURLs['en']; // } if (user.prefs.languages.primary && user.prefs.languages.primary != 'undefined') { if (indexURLs[user.prefs.languages.primary]) {// , indexURLs[user.prefs.languages.primary]; } else { return indexURLs['en']; } } } // showIndexPage(getUrlForUser(joeUser)); // : //( , , ) // -: , Maybe Currying const R = require('ramda'); const prop = R.prop; const path = R.path; const curry = R.curry; const Maybe = require('ramda-fantasy').Maybe; const getURLForUser = (user) => { return Maybe(user)// Maybe .map(path(['prefs', 'languages', 'primary'])) // Ramda .chain(maybeGetUrl); // maybeGetUrl URL null } const maybeGetUrl = R.curry(function(allUrls, language) {// return Maybe(allUrls[language]);// (url | null) })(indexURLs);// indexURLs function boot(user, defaultURL) { showIndexPage(getURLForUser(user).getOrElse(defaultURL)); } boot(joeUser, 'http://site.com/en'); //'http://site.com/sp'
æåã«ããã®ãœãªã¥ãŒã·ã§ã³ã§äœ¿çšãããFPã®æŠå¿µãšææ³ãåæããŸãããã
ãã¡ã³ã¯ã¿ãŒ
å€ãæ ŒçŽããmapã¡ãœãããå®è£
ããã¯ã©ã¹ïŒãŸãã¯ã³ã³ã¹ãã©ã¯ã¿ãŒé¢æ°ïŒãŸãã¯ããŒã¿åã¯ããã¡ã³ã¯ã¿ãŒãšåŒã°ããŸãã
ããšãã°ãé
åã¯å€ãæ ŒçŽã§ããæ ŒçŽãããå€ã«ïŒmapïŒé¢æ°ãé©çšã§ããmapã¡ãœããããããããé
åã¯ãã¡ã³ã¯ã¿ãŒã§ãã
const add1 = (a) => a+1; let myArray = new Array(1, 2, 3, 4);
ç¬èªã®ãã¡ã³ã¯ã¿ãŒ-MyFunctorãäœæããŸãããã ããã¯ãäœããã®å€ãæ ŒçŽããmapã¡ãœãããå®è£
ããJSã¯ã©ã¹ïŒã³ã³ã¹ãã©ã¯ã¿ãŒé¢æ°ïŒã«ãããŸããã ãã®ã¡ãœããã¯ãä¿åãããå€ã«é¢æ°ãé©çšããçµæããæ°ããMyfunctorãäœæããŠè¿ããŸãã
const add1 = (a) => a + 1; class MyFunctor { // constructor(value) { this.val = value; } map(fn) { // this.val + Myfunctor return new Myfunctor(fn(this.val)); } } //temp â , 1 let temp = new MyFunctor(1); temp.map(add1) //-> temp (map) "add1"
PS Functorã¯ããããã«å ããŠä»ã®ä»æ§ïŒ Fantasy-land ïŒãå®è£
ããå¿
èŠããããŸãããããã§ã¯è§ŠããŸããã
ã¢ãã
ã¢ããããã¡ã³ã¯ã¿ãŒã§ããã€ãŸãããããã¯mapã¡ãœãããæã£ãŠããŸã ã ãããã圌ãã¯åœŒã ãã§ã¯ãããŸããã äŸåé¢ä¿å³ãããäžåºŠèŠããšãã¢ããã¯ç°ãªãä»æ§ã®ç°ãªãé¢æ°ãããšãã°ã Apply ïŒ apã¡ãœããïŒã Applicative ïŒ ap and ofã¡ãœããïŒã Chain ïŒ ãã§ãŒã³ã¡ãœããïŒãå®è£
ããå¿
èŠãããããšãããããŸãã

ç°¡åãªèª¬æ ã JSã§ã¯ãã¢ããã¯ã¯ã©ã¹ãŸãã¯ã³ã³ã¹ãã©ã¯ã¿ãŒé¢æ°ã§ãããäžéšã®ããŒã¿ãæ ŒçŽããæå®ã«åŸã£ãŠæ ŒçŽãããããŒã¿ãåŠçããmapãapãofãããã³chainã¡ãœãããå®è£
ããŸãã
ã¢ããã®å
éšæ§é ãç解ããããã®ãµã³ãã«å®è£
ã§ãã
// â class Monad { constructor(val) { this.__value = val; } static of(val) {//Monad.of , new Monad(val) return new Monad(val); }; map(f) {// , ! return Monad.of(f(this.__value)); }; join() { // return this.__value; }; chain(f) {// , (map), return this.map(f).join(); }; ap(someOtherMonad) {// return someOtherMonad.map(this.__value); } }
éåžžãã¢ããã¯äžè¬çãªç®çã«ã¯äœ¿çšãããŸããããããå
·äœçã§æçšã§ãã ããšãã°ããMaybeããŸãã¯ãEtherãã
ãã¶ãã¢ãã
ãã¶ãã¢ããã¯ã¢ããä»æ§ãå®è£
ããã¯ã©ã¹ã§ãã ããããã¢ããã®ç¹åŸŽã¯ãnullãŸãã¯æªå®çŸ©ã®å€ãæ£ããåŠçããããšã§ãã
ç¹ã«ãæ ŒçŽãããããŒã¿ãnullãŸãã¯æªå®çŸ©ã®å Žåãmapé¢æ°ã¯ãã®é¢æ°ããŸã£ããå®è¡ããªããããnullããã³undefinedã®åé¡ã¯ãããŸããã ãã®ã¢ããã¯ãnullå€ãåŠçããå¿
èŠãããç¶æ³ã§äœ¿çšãããŸãã
以äžã®ã³ãŒãã¯ãMaybeã¢ããã®ramda-fantasyå®è£
ã瀺ããŠããŸãã å€ã«å¿ããŠã2ã€ã®ç°ãªããµãã¯ã©ã¹ã®ããããã®ã€ã³ã¹ã¿ã³ã¹ãäœæããŸã-JustãŸãã¯Nothing ïŒå€ã¯æçšãŸãã¯null /æªå®çŸ©ã®ããããã§ãïŒã
Justã¡ãœãããšNothingã¡ãœããã¯åãïŒmapãorElseãªã©ïŒã§ãããJustã¯äœããå®è¡ããNothingã¯äœãå®è¡ããŸããã
ãã®ã³ãŒãã®mapããã³orElseã¡ãœããã«ç¹ã«æ³šæããŠãã ããã
// Maybe ramda-fantasy // : https://github.com/ramda/ramda-fantasy/blob/master/src/Maybe.js function Maybe(x) { //<-- , Maybe Just Nothing return x == null ? _nothing : Maybe.Just(x); } function Just(x) { this.value = x; } util.extend(Just, Maybe); Just.prototype.isJust = true; Just.prototype.isNothing = false; function Nothing() {} util.extend(Nothing, Maybe); Nothing.prototype.isNothing = true; Nothing.prototype.isJust = false; var _nothing = new Nothing(); Maybe.Nothing = function() { return _nothing; }; Maybe.Just = function(x) { return new Just(x); }; Maybe.of = Maybe.Just; Maybe.prototype.of = Maybe.Just; // Just.prototype.map = function(f) { // map, Just , Just return this.of(f(this.value)); }; Nothing.prototype.map = util.returnThis; // <-- map, Nothing Just.prototype.getOrElse = function() { return this.value; }; Nothing.prototype.getOrElse = function(a) { return a; }; module.exports = Maybe;
Maybeã¢ããã䜿çšããŠnullãã§ãã¯ãåŠçããæ¹æ³ãèŠãŠã¿ãŸãããã
段éçã«è¡ããŸãããïŒ
- nullãŸãã¯nullããããã£ãæã€ããšãã§ãããªããžã§ã¯ããããå Žåãããããã¢ãããªããžã§ã¯ããäœæããŸãã
- Maybeã䜿çšããŠãã¢ããã®å
åŽãšå€åŽããå€ã«ã¢ã¯ã»ã¹ããramdajsã®ãããªã©ã€ãã©ãªã䜿çšããŠã¿ãŸãããã
- å®éã®å€ãnullã§ããããšãå€æããå Žåã¯ãããã©ã«ãå€ãæäŸããŸãïŒã€ãŸããnullãšã©ãŒãäºåã«åŠçããŸãïŒã
// 1. ... if (user == null) { // return indexURLs['en']; // } //: Maybe(user) // Maybe({userObj}) Maybe(null). Maybe // 2. ... if (user.prefs.languages.primary && user.prefs.languages.primary != 'undefined') { if (indexURLs[user.prefs.languages.primary]) {// , return indexURLs[user.prefs.languages.primary]; //: //, Maybe, map.path Ramda: <userMaybe>.map(path(['prefs', 'languages', 'primary'])) // 3. ... return indexURLs['en']; //hardcoded default values //: // Maybe- orElse getOrElse, , <userMayBe>.getOrElse('http://site.com/en')
ã«ãªãŒåïŒã°ããŒãã«ããŒã¿ãšãã«ããã©ã¡ãŒã¿ãŒé¢æ°ã®æäœã«åœ¹ç«ã¡ãŸãïŒ
ãã®ã»ã¯ã·ã§ã³ã§ã¯ãçŽé¢æ°ïŒçŽé¢æ°ïŒãšåæïŒåæïŒã«ã€ããŠèª¬æããŸãã
é¢æ°ã®ãã§ãŒã³-func1.func2.func3ãŸãã¯ïŒfunc1ïŒfunc2ïŒfunc3ïŒïŒïŒïŒãäœæãããå Žåããããã¯ãããã1ã€ã®ãã©ã¡ãŒã¿ãŒã®ã¿ãåãããšãã§ããŸããããšãã°ãfunc2ã2ã€ã®ãã©ã¡ãŒã¿ãŒ-func2ïŒparam1ãparam2ïŒãåãå Žåããã§ãŒã³ã«å«ããããšã¯ã§ããŸããïŒ
ãããå®éã«ã¯ãå€ãã®é¢æ°ã¯ããã€ãã®ãã©ã¡ãŒã¿ãŒãåããŸãã ã§ã¯ãããããã©ã®ããã«çµã¿åãããã®ã§ããããïŒ è§£æ±ºçïŒã«ãªãŒåã
ã«ãªãŒåãšã¯ãäžåºŠã«è€æ°ã®ãã©ã¡ãŒã¿ãŒãåãåãé¢æ°ãã1ã€ã®ãã©ã¡ãŒã¿ãŒã®ã¿ãåãåãé¢æ°ã«å€æããããšã§ãã ãã®é¢æ°ã¯ããã¹ãŠã®ãã©ã¡ãŒã¿ãŒãæž¡ããããŸã§å®è¡ãããŸããã
ããã«ãã°ããŒãã«å€æ°ã«ã¢ã¯ã»ã¹ãããšããã€ãŸããã¯ãªãŒã³ã«ãå®è¡ãããšãã«ã«ãªãŒåã䜿çšã§ããŸãã
ããäžåºŠãœãªã¥ãŒã·ã§ã³ãèŠãŠã¿ãŸãããã
// , // indexURLs let indexURLs = { 'en': 'http://mysite.com/en', //English 'sp': 'http://mysite.com/sp', //Spanish 'jp': 'http://mysite.com/jp' //Japanese } // const getUrl = (language) => allUrls[language]; //, ( ), // // : const getUrl = (allUrls, language) => { return Maybe(allUrls[language]); } // : const getUrl = R.curry(function(allUrls, language) {//curry return Maybe(allUrls[language]); }); const maybeGetUrl = getUrl(indexURLs) // curried . // maybeGetUrl (). maybe(user).chain(maybeGetUrl).bla.bla
äŸ2.ãšã©ãŒãã¹ããŒããé¢æ°ãæäœãããšã©ãŒãçºçããçŽåŸã«çµäºãã
ãã®ã»ã¯ã·ã§ã³ã§ã¯ã次ã®ããšã«ã€ããŠèª¬æããŸãã
Nullãšã©ãŒã眮ãæãããããã©ã«ããå€ãããå Žåãããããã¢ããã¯éåžžã«äŸ¿å©ã§ãã ããããå®éã«ãšã©ãŒãã¹ããŒããå¿
èŠãããé¢æ°ã¯ã©ãã§ããããïŒ ãŸãããšã©ãŒãã¹ããŒããè€æ°ã®é¢æ°ããã§ãŒã³ã«ãŸãšãããšãã©ã®é¢æ°ããšã©ãŒãã¹ããŒããããã©ã®ããã«ããŠç¥ãããšãã§ããŸããïŒ ã€ãŸããé«éé害ïŒé«éé害ïŒãå¿
èŠã§ãã
ããšãã°ããã§ãŒã³func1.func2.func3 ...ãããã func2ããšã©ãŒãã¹ããŒããå Žåã¯ã func3ãšæ¬¡ã®é¢æ°ãã¹ãããããããã«åŠçããããã«func2ãããšã©ãŒãæ£ãã衚瀺ããå¿
èŠããããŸãã
ã¢ãããããã
ããããã®ã¢ããã¯ããšã©ãŒãçºçããŠããã«çµäºãããå Žåã«ããšã©ãŒãçºçããå Žæãæ£ç¢ºã«ç¹å®ã§ããããã«ãè€æ°ã®é¢æ°ãæäœããã®ã«æé©ã§ãã
ã¢ããªã±ãŒã·ã§ã³ïŒä»¥äžã®åœä»€ã³ãŒãã§ã¯ã ã¢ã€ãã ã® çšéãšå²åŒãèšç®ãã showTotalPriceã衚瀺ããŸã ã
äŸ¡æ Œå€ãæ°å€ã§ãªãå Žåãçšé¢æ°ã¯ãšã©ãŒãã¹ããŒããããšã«æ³šæããŠãã ããã åãçç±ã§ãå²åŒæ©èœããšã©ãŒãã¹ããŒããŸãã ãã ããããã«ãã¢ã€ãã ã®äŸ¡æ Œã10æªæºã®å Žåãå²åŒã¯ãšã©ãŒãã¹ããŒããŸãã
ãããã£ãŠã showTotalPriceã¯ãšã©ãŒããã§ãã¯ããŸãã
// // , const tax = (tax, price) => { if (!_.isNumber(price)) return new Error("Price must be numeric"); return price + (tax * price); }; // , const discount = (dis, price) => { if (!_.isNumber(price)) return (new Error("Price must be numeric")); if (price < 10) return new Error("discount cant be applied for items priced below 10"); return price - (price * dis); }; const isError = (e) => e && e.name == 'Error'; const getItemPrice = (item) => item.price; // . . const showTotalPrice = (item, taxPerc, disount) => { let price = getItemPrice(item); let result = tax(taxPerc, price); if (isError(result)) { return console.log('Error: ' + result.message); } result = discount(discount, result); if (isError(result)) { return console.log('Error: ' + result.message); } // console.log('Total Price: ' + result); } let tShirt = { name: 't-shirt', price: 11 }; let pant = { name: 't-shirt', price: '10 dollars' }; let chips = { name: 't-shirt', price: 5 }; //less than 10 dollars error showTotalPrice(tShirt) // : 9,075 showTotalPrice(pant) // : showTotalPrice(chips) // : 10
ããããã®ã¢ããã§showTotalPriceãæ¹åããFPã¹ã¿ã€ã«ã§ãã¹ãŠãæžãæããæ¹æ³ãèŠãŠã¿ãŸãããã
ããããã®ã¢ããã«ã¯ãEther.LeftãšBelow.Rightã®2ã€ã®ã³ã³ã¹ãã©ã¯ã¿ãŒããããŸãã ããããã®ãµãã¯ã©ã¹ãšèŠãªãããšãã§ããŸãã å·Šãšå³ã¯ã¢ããã§ãïŒ ã¢ã€ãã¢ã¯ããšã©ãŒ/äŸå€ãå·Šã«ãæçšãªå€ãå³ã«ä¿åããããšã§ãã ã€ãŸããå€ã«å¿ããŠãEther.LeftãŸãã¯Below.Rightã®ã€ã³ã¹ã¿ã³ã¹ãäœæããŸãã ããããããšã§ããããã®å€ã«ãããããã§ãŒã³ãªã©ãé©çšã§ããŸãã
LeftãšRightã®äž¡æ¹ããããããã§ãŒã³ãªã©ãæäŸããŸããã Leftã³ã³ã¹ãã©ã¯ã¿ãŒã¯ãšã©ãŒã®ã¿ãä¿åããRightã³ã³ã¹ãã©ã¯ã¿ãŒã¯å®éã®çµæãä¿åããããããã¹ãŠã®é¢æ°ãå®è£
ããŸãã
ã§ã¯ãåœä»€åã³ãŒããé¢æ°åã«å€æããæ¹æ³ãèŠãŠã¿ãŸãããã
æé 1.æ»ãå€ãå·Šãšå³ã«ã©ããããŸãã
泚ïŒãã©ãããã¯ãäœããã®ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãäœæãããããšãæå³ããŸãã å
éšã®ãããã®é¢æ°ã¯newãåŒã³åºãããããããè¡ãå¿
èŠã¯ãããŸããã
var Either = require('ramda-fantasy').Either; var Left = Either.Left; var Right = Either.Right; const tax = R.curry((tax, price) => { if (!_.isNumber(price)) return Left(new Error("Price must be numeric")); //<-- Either.Left return Right(price + (tax * price)); //<-- Either.Right }); const discount = R.curry((dis, price) => { if (!_.isNumber(price)) return Left(new Error("Price must be numeric")); //<--Wrap Error in Either.Left if (price < 10) return Left(new Error("discount cant be applied for items priced below 10")); //<-- Either.Left return Right(price - (price * dis)); //<-- Either.Right });
ã¹ããã2.å
ã®å€ãRightã§ã©ããããŸããããã¯æå¹ã§ãããçµåïŒæ§æïŒã§ããããã§ãã
const getItemPrice = (item) => Right(item.price);
ã¹ããŒãž3ã2ã€ã®é¢æ°ãäœæããŸãã1ã€ã¯ãšã©ãŒãåŠçãããã1ã€ã¯çµæãåŠçããŸãã ïŒ ramda -fantasy.js apiãã ïŒ Either.eitherã§ã©ããããŸãã
Both.eitherã¯ãçµæãã³ãã©ãŒããšã©ãŒãã³ãã©ãŒã Etherã¢ããã® 3ã€ã®ãã©ã¡ãŒã¿ãŒãåããŸãã ã©ã¡ãããã«ãªãŒåãããŠããããããã³ãã©ãŒãããã«æž¡ãããšãã§ããŸãã
ã©ã¡ããã3ã€ãã¹ãŠã®ãã©ã¡ãŒã¿ãŒãåãåããšããã«ãã©ã¡ãããå³ãå·Šãã«ãã£ãŠãçµæãã³ãã©ãŒãŸãã¯ãšã©ãŒãã³ãã©ãŒã®ããããã«ã©ã¡ãããæž¡ããŸãã
const displayTotal = (total) => { console.log('Total Price: ' + total) }; const logError = (error) => { console.log('Error: ' + error.message); }; const eitherLogOrShow = Either.either(logError, displayTotal);
ã¹ããã4. chainã¡ãœããã䜿çšããŠããšã©ãŒãã¹ããŒããé¢æ°ãçµã¿åãããŸãã çµæãBoth.eitherïŒeitherLogOrShowïŒã«æž¡ããçµæãŸãã¯ãšã©ãŒãã³ãã©ãŒã«ãªã¬ãŒããŸãã
const showTotalPrice = (item) => eitherLogOrShow(getItemPrice(item).chain(apply25PercDisc).chain(addCaliTax))
ãã¹ãŠããŸãšããïŒ
const tax = R.curry((tax, price) => { if (!_.isNumber(price)) return Left(new Error("Price must be numeric")); return Right(price + (tax * price)); }); const discount = R.curry((dis, price) => { if (!_.isNumber(price)) return Left(new Error("Price must be numeric")); if (price < 10) return Left(new Error("discount cant be applied for items priced below 10")); return Right(price - (price * dis)); }); const addCaliTax = (tax(0.1));// 10 % const apply25PercDisc = (discount(0.25));// 25 % const getItemPrice = (item) => Right(item.price); const displayTotal = (total) => { console.log('Total Price: ' + total) }; const logError = (error) => { console.log('Error: ' + error.message); }; const eitherLogOrShow = Either.either(logError, displayTotal);
äŸ3.æœåšçãªNULLãªããžã§ã¯ããžã®å€ã®å²ãåœãŠ
***䜿çšãããFPã³ã³ã»ããïŒapplicativeïŒApplicativeïŒ
ã¢ããªã±ãŒã·ã§ã³ïŒãŠãŒã¶ãŒããã°ã€ã³ããŠãããããã¢ãŒã·ã§ã³ãæå¹ãªå ŽåïŒã€ãŸããå²åŒãããå ŽåïŒããŠãŒã¶ãŒã«å²åŒãäžããå¿
èŠããããšããŸãã

applyDiscountã¡ãœããã䜿çšããŸãã ãŠãŒã¶ãŒïŒå·ŠåŽïŒãŸãã¯å²åŒïŒå³åŽïŒãnullã®å Žåãnullãšã©ãŒãã¹ããŒã§ããŸãã
// user, , . //Null- , null. const applyDiscount = (user, discount) => { let userClone = clone(user);// - userClone.discount = discount.code; return userClone; }
applicativeã䜿çšããŠãããã©ã®ããã«è§£æ±ºã§ããããèŠãŠã¿ãŸãããã
é©çšæ§
apã¡ãœãããæã¡ãApplicativeä»æ§ãå®è£
ããã¯ã©ã¹ã¯ãã¹ãŠapplicativeãšåŒã°ããŸãã ãã®ãããªã¯ã©ã¹ã¯ãæ¹çšåŒã®å·ŠåŽïŒãŠãŒã¶ãŒïŒãšå³åŽïŒå²åŒïŒã®äž¡æ¹ã§NULLå€ãåŠçããé¢æ°ã§äœ¿çšã§ããŸãã
ïŒãã¹ãŠã®ã¢ãããšåæ§ã«ïŒã¢ãããapä»æ§ãå®è£
ããŠããå¯èœæ§ããããŸãã ãããã£ãŠãæ©èœã¬ãã«ã§ã¯ãMaybeã¢ããã䜿çšããŠnullãæäœã§ããŸãã é©çšå¯èœãªãã®ãšããŠäœ¿çšãããMaybeã¢ããã§applyDiscountãæ©èœãããæ¹æ³ãèŠãŠã¿ãŸãããã
ã¹ããã1.æœåšçãªãã«å€ãå€åã¢ããã§ã©ããããŸãã
const maybeUser = Maybe(user); const maybeDiscount = Maybe(discount);
ã¹ããã2.é¢æ°ãæžãçŽããäžåºŠã«1ã€ã®ãã©ã¡ãŒã¿ãŒãæž¡ãããã«ã«ãªãŒåããŸãã
// , // var applyDiscount = curry(function(user, discount) { user.discount = discount.code; return user; });
ã¹ããã3.æåã®åŒæ°ïŒmaybeUserïŒãmapçµç±ã§applyDiscountã«æž¡ããŸãã
// map (maybeUser) applyDiscount const maybeApplyDiscountFunc = maybeUser.map(applyDiscount); // , applyDiscount map , (maybeApplyDiscountFunc) Maybe applyDiscount, maybeUser( ).
ã€ãŸããã¢ããã«ã©ãããããé¢æ°ãã§ããŸããïŒ
ã¹ããŒãž4ãmaybeApplyDiscountFuncã䜿çšããŸãã
ãã®æç¹ã§ãå€åApplyDiscountFuncã¯æ¬¡ã®ããã«ãªããŸãã
1ïŒå€åã©ãããããé¢æ°-ãŠãŒã¶ãŒãååšããå Žå;
2ïŒãªãïŒMaybeã®ãµãã¯ã©ã¹ïŒ-ãŠãŒã¶ãŒãååšããªãå Žåã
ãŠãŒã¶ãŒãååšããªãå ŽåãNothingãè¿ããããã®åŸã®ãŠãŒã¶ãŒãšã®ãã¹ãŠã®ã¢ã¯ã·ã§ã³ã¯å®å
šã«ç¡èŠãããŸãã ãããã£ãŠã2çªç®ã®åŒæ°ãæž¡ããšãäœãèµ·ãããŸããã nullãšã©ãŒããããŸããã
ãŠãŒã¶ãŒãååšããå Žåãé¢æ°ãå®è¡ããããã«mapãä»ããŠmaybeApplyDiscountFuncã«2çªç®ã®åŒæ°ãæž¡ãããšããããšãã§ããŸãã
maybeDiscount.map(maybeApplyDiscountFunc)! // !
çãïŒmapã¯ãé¢æ°ïŒmaybeApplyDiscountFuncïŒãMaybeå
ã«ãããšãã«ããã®å®è¡æ¹æ³ãç¥ããªãïŒ
ãããã£ãŠããã®ã·ããªãªã§åäœããã«ã¯å¥ã®ã€ã³ã¿ãŒãã§ã€ã¹ãå¿
èŠã§ãã ãããŠããã®ã€ã³ã¿ãŒãã§ãŒã¹ã¯apã§ãïŒ
ã¹ããã5. apé¢æ°æ
å ±ãæŽæ°ããŸãã apã¡ãœããã¯å¥ã®Maybeã¢ãããåãåããçŸåšä¿åããŠããé¢æ°ãæž¡ããŸã/é©çšããŸãã
class Maybe { constructor(val) { this.val = val; } ... ...
以äžã«ç€ºãããã«ãmapã䜿çšãã代ããã«ãmaybeApplyDiscountFuncãmaybeDiscountã«é©çšïŒapïŒããã ãã§ãã ãããŠãããã¯ããŸããããŸãïŒ
maybeApplyDiscountFunc.ap(maybeDiscount) // applyDiscount this.val maybeApplyDiscountFunc: maybeDiscount.map(applyDiscount) //, maybeDiscount , . maybeDiscount Null, .
詳现ã«ã€ããŠã¯ãæããã«ããã¡ã³ã¿ãžãŒã©ã³ãã®ä»æ§ãå€æŽãããŠããŸãã å€ãããŒãžã§ã³ã§ã¯ã次ã®ããã«èšè¿°ããå¿
èŠããããŸãããJustïŒfïŒ.apïŒJustïŒxïŒïŒãããã§fã¯é¢æ°ãxã¯å€ã§ãã æ°ããããŒãžã§ã³ã§ã¯ãJustïŒxïŒ.apïŒJustïŒfïŒïŒãèšè¿°ããå¿
èŠããããŸãã ãããã売äžã®å€§éšåã¯ãŸã å€ãã£ãŠããŸããã ã±ã€ã¿ã¬ã¯ãµã³ããŒã«æè¬ããŸã ã
ãŸãšãããšã è€æ°ã®ãã©ã¡ãŒã¿ãŒã§æ©èœããé¢æ°ããããããããããã«ã®å Žåãæåã«ã«ã¬ãŒãã次ã«Maybeå
ã«é
眮ããŸãã ãŸãããã¹ãŠã®ãã©ã¡ãŒã¿ãŒãMaybeã«å
¥ããapã䜿çšããŠé¢æ°ãå®è¡ããŸãã
CurryNé¢æ°
ç§ãã¡ã¯ãã§ã«ã«ã¬ãŒã«ç²ŸéããŠããŸãã ããã¯é¢æ°ã®åçŽãªå€æã§ãããããäžåºŠã«è€æ°ã®åŒæ°ããšãã®ã§ã¯ãªããäžåºŠã«1ã€ã®åŒæ°ãåããŸãã
// : const add = (a, b) =>a+b; const curriedAdd = R.curry(add); const add10 = curriedAdd(10);// . , (b) . // . add10(2) // -> 12 // add 10 2.
ãããã2ã€ã®æ°åãè¿œå ãã代ããã«ã addé¢æ°ãåŒæ°ãšããŠæž¡ããããã¹ãŠã®æ°åãåèšã§ãããšãããã©ãã§ããããïŒ
const add = (...args) => R.sum(args); //
curryNã䜿çšããŠåŒæ°ã®æ°ãå¶éããããšã«ããããããã«ãªãŒåã§ããŸãã
// curryN const add = (...args) => R.sum(args); // CurryN: const add = (...args) => R.sum(args); const add3Numbers = R.curryN(3, add); const add5Numbers = R.curryN(5, add); const add10Numbers = R.curryN(10, add); add3Numbers(1,2,3) // 6 add3Numbers(1) // , . add3Numbers(1, 2) // , .
curryNã䜿çšããŠé¢æ°åŒã³åºãã®æ°ãåŸ
ã€
ãã°ã3ååŒã³åºãããšãã«ã®ã¿ãã°ã«æžã蟌ãé¢æ°ãå¿
èŠã ãšããŸãïŒ1åãš2åã®åŒã³åºãã¯ç¡èŠãããŸãïŒã äŸïŒ
// let counter = 0; const logAfter3Calls = () => { if(++counter == 3) console.log('called me 3 times'); } logAfter3Calls() // logAfter3Calls() // logAfter3Calls() // 'called me 3 times'
curryNã䜿çšããŠãã®åäœããšãã¥ã¬ãŒãã§ããŸãã
// const log = () => { console.log('called me 3 times'); } const logAfter3Calls = R.curryN(3, log); // logAfter3Calls('')('')('')//'called me 3 times' //: '', CurryN .
泚ïŒãã®ææ³ã¯ãé©çšå¯èœãªæ€èšŒã«äœ¿çšããŸãã
äŸ4.ãšã©ãŒã®åéãšè¡šç€º
ãã®ã»ã¯ã·ã§ã³ã§ã¯ãæ€èšŒïŒãã¡ã³ã¯ã¿ãŒæ€èšŒãã¢ããªã«ãã£ãæ€èšŒãã¢ããæ€èšŒïŒã«ã€ããŠèª¬æããŸãã
Validation (Validation Applicative) , ap (apply).
Either , . Either, chain, Validation ap. , chain , ap, Validation, .
, .
: , , (isUsernameValid, isPwdLengthCorrect ieEmailValid). , .

Validation. , Validation.
folktalejs data.validation, ramda-fantasy . Either : Success Failure . , Either.
1. Success Failure (. . ).
const Validation = require('data.validation') // folktalejs const Success = Validation.Success const Failure = Validation.Failure const R = require('ramda'); //: function isUsernameValid(a) { return /^(0|[1-9][0-9]*)$/.test(a) ? ["Username can't be a number"] : a } //: function isUsernameValid(a) { return /^(0|[1-9][0-9]*)$/.test(a) ? Failure(["Username can't be a number"]) : Success(a) }
, .
2. (dummy function) .
const returnSuccess = () => 'success';// success
3. curryN ap
ap : ( ), .
, ap. , monad1 . monad1.ap(monad2), . . resultingMonad , , ap monad3.
let finalResult = monad1.ap(monad2).ap(monad3) // let resultingMonad = monad1.ap(monad2) let finalResult = resultingMonad.ap(monad3) // , monad1 , monad1.ap(monad2) (resultingMonad)
, ap, , .
, ap.
, - .
Success(returnSuccess) .ap(isUsernameValid(username)) // .ap(isPwdLengthCorrect(pwd))// .ap(ieEmailValid(email))//
, Success(returnSuccess).ap(isUsernameValid(username)) . ap .
curryN, , N .
success .
function validateForm(username, pwd, email) { //3 â ap . let success = R.curryN(3, returnSuccess); return Success(success)// ; ap .ap(isUsernameValid(username)) .ap(isPwdLengthCorrect(pwd)) .ap(ieEmailValid(email)) }
:
const Validation = require('data.validation') // folktalejs const Success = Validation.Success const Failure = Validation.Failure const R = require('ramda'); function isUsernameValid(a) { return /^(0|[1-9][0-9]*)$/.test(a) ? Failure(["Username can't be a number"]) : Success(a) } function isPwdLengthCorrect(a) { return a.length == 10 ? Success(a) : Failure(["Password must be 10 characters"]) } function ieEmailValid(a) { var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(a) ? Success(a) : Failure(["Email is not valid"]) } const returnSuccess = () => 'success';// success function validateForm(username, pwd, email) { let success = R.curryN(3, returnSuccess);// 3 â ap . return Success(success) .ap(isUsernameValid(username)) .ap(isPwdLengthCorrect(pwd)) .ap(ieEmailValid(email)) } validateForm('raja', 'pwd1234567890', 'r@r.com').value; //Output: success validateForm('raja', 'pwd', 'r@r.com').value; //Output: ['Password must be 10 characters' ] validateForm('raja', 'pwd', 'notAnEmail').value; //Output: ['Password must be 10 characters', 'Email is not valid'] validateForm('123', 'pwd', 'notAnEmail').value; //['Username can\'t be a number', 'Password must be 10 characters', 'Email is not valid']