[Functional Programming] Async IO Functor
We will see a peculiar example of a pure function. This function contained a side-effect, but we dubbed it pure by wrapping its action in another function. Here's another example of this:
// getFromStorage :: String -> (_ -> String) const getFromStorage = key => () => localStorage[key];
'localStorage' is a side effect, but we wrap into a function call, so what 'getFromStorage' return is not a value but a function. And this returned function is waiting to be called, before calling, we can do all kind of function mapping of composion on it, that's the beautity.
Let's see how to define a IO functor for this side-effect code:
class IO { static of(x) { return new IO(() => x); } constructor(fn) { this.$value = fn; } map(fn) { return new IO(compose(fn, this.$value)); } inspect() { return `IO(${this.$value})`; } }
IO will take a function as input.
Example of IO:
( Note: the reuslt shown in comment is not actual result, the actual result is wrapped in {$value: [Function]}, just for now, we show the result which should be in the end.)
// ioWindow :: IO Window const ioWindow = new IO(() => window); ioWindow.map(win => win.innerWidth); // IO(1430) ioWindow .map(prop('location')) .map(prop('href')) .map(split('/')); // IO(['http:', '', 'localhost:8000', 'blog', 'posts'])
There is some library already define the IO functor for us, we don't need to write one for our own, for example, Async from Crocks.js, or Data.Task from Folktale.
Here we are using 'Data.Task' as an example:
// -- Node readFile example ------------------------------------------ const fs = require('fs'); // readFile :: String -> Task Error String const readFile = filename => new Task((reject, result) => { fs.readFile(filename, (err, data) => (err ? reject(err) : result(data))); }); readFile('metamorphosis').map(split('\n')).map(head); // Task('One morning, as Gregor Samsa was waking up from anxious dreams, he discovered that // in bed he had been changed into a monstrous verminous bug.') // -- jQuery getJSON example ----------------------------------------- // getJSON :: String -> {} -> Task Error JSON const getJSON = curry((url, params) => new Task((reject, result) => { $.getJSON(url, params, result).fail(reject); })); getJSON('/video', { id: 10 }).map(prop('title')); // Task('Family Matters ep 15') // -- Default Minimal Context ---------------------------------------- // We can put normal, non futuristic values inside as well Task.of(3).map(three => three + 1); // Task(4)
To run the Task, we need to call 'fork(rej, res)':
// -- Pure application ------------------------------------------------- // blogPage :: Posts -> HTML const blogPage = Handlebars.compile(blogTemplate); // renderPage :: Posts -> HTML const renderPage = compose(blogPage, sortBy(prop('date'))); // blog :: Params -> Task Error HTML const blog = compose(map(renderPage), getJSON('/posts')); // -- Impure calling code ---------------------------------------------- blog({}).fork( error => $('#error').html(error.message), page => $('#main').html(page), );
Example of Either & IO:
// validateUser :: (User -> Either String ()) -> User -> Either String User const validateUser = curry((validate, user) => validate(user).map(_ => user)); // save :: User -> IO User const save = user => new IO(() => ({ ...user, saved: true })); const validateName = ({ name }) => (name.length > 3 ? Either.of(null) : left('Your name need to be > 3') ); const saveAndWelcome = compose(map(showWelcome), save); const register = compose( either(IO.of, saveAndWelcome), validateUser(validateName), );
【推荐】国内首个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工具
2017-02-23 [HTML5] Focus management using CSS, HTML, and JavaScript
2017-02-23 [TypeScript] Increase TypeScript's type safety with noImplicitAny
2017-02-23 [React] displayName for stateless component
2016-02-23 [Protractor] Locators and Suites in Protractor