[Functional Programming] Working with two functors(Applicative Functors)-- Part1 --.ap
What is applicative functor:
the ability to apply functors to each other.
For example we have tow functors: Container(2), Container(3)
// We can't do this because the numbers are bottled up. add(Container.of(2), Container.of(3)); // NaN
We cannot just add two functors!
Instead we should do:
const map = (fn, m) => m.map(fn);
const containerOfAdd2 = map(add(3), Container.of(2)); // Container(5)
or
Container.of(2).chain(two => Container.of(3).map(add(two)));
Previous solution should work. but there are better way to do it:
1. ap
Container.prototype.ap = function (otherContainer) { return otherContainer.map(this.$value); };
As you can see, 'ap' takes a fuctor then applya map to it.
We can see ap:
Container.of(2).map(add).ap(Container.of(3)); // Container(5)
Or, we add lift 'add(2)' into Container, then apply Container(3):
Container.of(add(2)).ap(Container.of(3)); // Container(5)
Because 'add' is partially applied in add(2), when doing '.ap(Container.of(3))', we give the rest input '3' to it.
Now, we can define applicative functor in programming language:
An applicative functor is a pointed functor with an
ap
method
Note the dependence on pointed.
Laws behind:
F.of(x).map(f) === F.of(f).ap(F.of(x))
Main idea is: lift 'f' (function) into Functor, then 'ap' (apply) another Functor with the value (x).
Some example:
Maybe.of(add).ap(Maybe.of(2)).ap(Maybe.of(3)) // Just(5) Task.of(add).ap(Task.of(2)).ap(Task.of(3)) // Task(5)
Equals:
Maybe.of(add(2)).ap(Maybe.of(3)) // Just(5) Task.of(add(2)).ap(Task.of(3)) // Task(5)
More examples:
// Http.get :: String -> Task Error HTML const renderPage = curry((destinations, events) => { /* render page */ }); Task.of(renderPage).ap(Http.get('/destinations')).ap(Http.get('/events')); // Task("<div>some page with dest and events</div>")
// $ :: String -> IO DOM const $ = selector => new IO(() => document.querySelector(selector)); // getVal :: String -> IO String const getVal = compose(map(prop('value')), $); // signIn :: String -> String -> Bool -> User const signIn = curry((username, password, rememberMe) => { /* signing in */ }); IO.of(signIn).ap(getVal('#email')).ap(getVal('#password')).ap(IO.of(false)); // IO({ id: 3, email: 'gg@allin.com' })
----
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | const R = require( 'ramda' ); class Container { static of(x) { return new Container(x); } constructor(x) { this .$value = x; } map (fn) { return Container.of(fn( this .$value)); } ap (functor) { return functor.map( this .$value); } join () { return this .$value; } chain(fn) { return this .map(fn). join (); } inspect() { return `Container(${ this .$value})`; } } class Maybe { get isNothing() { return this .$value === null || this .$value === undefined; } get isJust() { return ! this .isNothing; } constructor(x) { this .$value = x; } inspect() { return this .isNothing ? 'Nothing' : `Just(${ this .$value})`; } // ----- Pointed Maybe static of(x) { return new Maybe(x); } // ----- Functor Maybe map(fn) { return this .isNothing ? this : Maybe.of(fn( this .$value)); } // ----- Applicative Maybe ap(f) { return this .isNothing ? this : f.map( this .$value); } // ----- Monad Maybe chain(fn) { return this .map(fn). join (); } join () { return this .isNothing ? this : this .$value; } // ----- Traversable Maybe sequence(of) { this .traverse(of, identity); } traverse(of, fn) { return this .isNothing ? of( this ) : fn( this .$value).map(Maybe.of); } } const add = a => b => a + b; const map = (fn, m) => m.map(fn); const notWorking = add(Container.of(2), Container.of(3)); const containerOfAdd2 = map(add(3), Container.of(2)); console.log(containerOfAdd2); // Contianer(5) const works = Container.of(2).chain(v => Container.of(3).map(add(v))); console.log(works); // Contianer(5) const ap = Container.of(2).map(add).ap(Container.of(3)); console.log(ap) const ap2 = Container.of(add(2)).ap(Container.of(3)); console.log(Maybe.of(add).ap(Maybe.of(2)).ap(Maybe.of(3))) console.log(Maybe.of(add(2)).ap(Maybe.of(3))) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2018-02-25 [ES2018] Two ways to write for-await-of
2016-02-25 [RxJS] Using Observable.create for fine-grained control