Earlier quoted context omitted.
the speed you gain by being able to whip up a feature in 10 minutes is worth the pain of an occasional dead link This is everything that is wrong with the software industry, summarized in one sentence. Speed gain enjoyed by developers is paid for by the users in pain. It used to be that developers would go through tremendous amounts of pain just to squeeze out a few instructions from a UI drawing routine in order to…
Not at all. I share your frustration. Closures on a server are a powerful way of representing data flows. But they come at a cost: the links expire after some time. How do you strike a balance? The simplest way is to put in the extra time to make everything into a persistent link. But, that's equivalent to removing all the benefits of lexical scoping. If you've ever created an inner function before, you know how powe…
1. inside of your express endpoint, create a closure that captures some state. For example, the user's IP address.
(This is a dumb example, but the point is that you can capture whatever state you want. Some cases are mentioned here: http://paulgraham.com/road.html and here: http://ep.yimg.com/ty/cdn/paulgraham/bbnexcerpts.txt)
EDIT: I updated this to capture the date + time the original page was loaded, which is slightly more compelling than a mere IP address.
app.get('/', function (req, res) {
let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
let now = new Date();
let date = now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate();
let time = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds();
let fn = (req, res) => {
res.send(`hello ${req.query.name}. On ${date} at {time}, your IP address was ${ip}`)
}
... see below ...
})
2. insert that closure into a global hash table, keyed by a random ID. g_fnids = {};
app.get('/', function (req, res) {
let fn = ...
let id =
g_fnids[id] = fn;
res.send(`Say hello`);
})
3. create an endpoint called /x which works like `/x?fnid=&foo=1&bar=2`. Use to look up the closure. Call the closure, passing the request to it: app.get('/x', function (req, res) {
let id = req.query.fnid;
let fn = g_fnids[id];
fn(req, res)
}
Done.Congratulations, your closure is now an express endpoint. Except you didn't have to name it. You can link users to it like `&name=bob">Say hello`.
The reason this is a powerful technique is that you can use it with forms. The form target can be /x, and the query params are whatever the user types into the form fields.
I bet you already see a few interesting use cases. And you might notice that this makes scaling the server a little more difficult, since incoming requests have to be routed to the server containing the actual closure. But in the meantime, you now have "inner functions" in your web framework. It makes implementing password reset functionality completely trivial, and no database required.
If it seems slightly annoying to use – "I thought you said this was a productivity boost. But it's annoying to type all of that!" – lisp macros hide all of this boilerplate code, so there's zero extra typing. You can get lisp macros for Javascript using Lumen lisp: https://github.com/sctb/lumen
Even without macros, though, I bet this technique is shorter. Suppose you had to store the date + time + IP address somewhere. Where would you put it? I assume some sort of nosql database like firebase. But wouldn’t that code be much longer and more annoying to write? So this technique has tremendous value, and I’m amazed no one is using it circa 2020.