@seiyab/do-expr
v1.0.1
Published
do expression as a package inspired by https://tc39.es/proposal-do-expressions/
Readme
package of do expressions
This package is inspired by proposal-do-expressions
- https://github.com/tc39/proposal-do-expressions
- https://tc39.github.io/proposal-do-expressions/
Motivation
- expression-oriented programming one of the great advances of FP
- expressions plug together like legos, making more malleable programming experience in-the-small
Installation
npm install @seiyab/do-exprExamples
Write in an expression-oriented style, scoping variables as locally as possible:
import { do_ } from "@seiyab/do-expr";
let x = do_(() => {
let tmp = f();
return tmp * tmp + 1
});Use conditional statements as expressions, instead of awkward nested ternaries:
let x = do_(() => {
if (foo()) { return f() }
else if (bar()) { return g() }
else { return h() }
});Especially nice for templating languages like JSX:
return (
<nav>
<Home />
{do_(() => {
if (loggedIn) return <LogoutButton />;
return <LoginButton />;
})}
</nav>
)Edge cases
var declarations
var declarations are allowed, with the hoisting to the parameter function's scope.
This behavior differs from proposal-do-expressions.
Empty do
do_(() => {}) is almost equivalent to undefined.
Some lint tool might blame it.
await
Works fine.
To use await, you need an await before do_ and an async before ().
const result = await do_(async () => {
const x = await fetchSomeData();
if (x.isSuccess) return "ok";
return "oops";
});throw
Works fine. Does what you expect.
break/continue/yield
These won't work like proposal-do-expressions.
The major reason is technical one. And it's also for readability. Allowing these might induce confusion. These don't fit the motivation "expression-oriented programming".
return
It has a different functionality from proposal-do-expressions.
return is used for declaring result of do_, rather than returning from outer function.
Conflict with do-while
No problem at all.
Comparison
vs. IIFE
Yes, do-expr is just IIFE as you noticed.
do-expr still has some advantages.
The main advantage is readability. We need to read last part of the expressions to know whether expressions are IIFEs or not.
// plain IIFE
const x = (() => {
// ^^^^^ is x a function?
if (f()) return g();
return h();
})()
//^^ Finally I got it. this is an IIFE.
// do_
const x = do_(() => {
// ^^^ OK, this behaves like a do expression.
if (f()) return g();
return h();
})As another idea, you can declare _do argument to indicate it behaves like do expression.
const x = ((_do) => {
// ^^^ OK, this behave like a do expression.
if (f()) return g();
return h();
})()In this case, you need to write coding guideline documents and notify colleagues.
Because this is not idiomatic yet.
If readers don't know this idiom, they are confused by a mysterious _do argument and might consider that it is a function declaration until they reach the last ().
On the other hand, do_ already has README.md that you read now.
If readers don't know do_, they are likely to google @seiyab/do-expr and find this README.
vs. proposal-do-expressions
Functionality
Some features like break / continue / yield doesn't work with @seiyab/do-expr.
The reasons are not only technical ones.
It is an intended interface design.
The main motivation of @seiyab/do-expr is expression-oriented programming.
Though the features are perhaps helpful, they don't fit the original motivation.
They will induce unnecessary complexity.
The following issue reports proposal-do-expressions vs IIFEs: https://github.com/tc39/proposal-do-expressions/issues/34
Availability
proposal-do-expressions is still stage 1 of the TC39 process.
But a babel plugin @babel/plugin-proposal-do-expression is available.
@seiyab/do-expr already has stable release.
