Closed davidchambers closed 6 years ago
I hadn't seen maybe callbacks, but one approach is to put all arguments in an object with some keys optional.
On Dec 18, 2017 10:47 AM, "David Komer" notifications@github.com wrote:
Re: commas, I find it also removes some friction in thinking where to place them for multi-line calls. One less thing to think about :)
One issue I've come up against though is for optional parameters. Is the idiomatic way to deal with it to always wrap in a Maybe? e.g.
const foo = arg => maybeCallback => { //do stuff with arg, get result S.map (c => c(result)) (maybeCallback); }
foo (bar) (S.Nothing); foo (baz) (S.Just(myCallback));
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sanctuary-js/sanctuary/issues/438#issuecomment-352375928, or mute the thread https://github.com/notifications/unsubscribe-auth/AC6uxZAPtsNNBh-xBoz5xP86-F-r8-Heks5tBjSjgaJpZM4PKLTy .
Is there a https://prettier.io configuration for this function application spacing style? I think it goes against adoption if we help people to configure their automatic code formatters accordingly. I'm just using the built-in IntelliJ settings, btw, but I would consider switching to prettier.
I just noticed https://github.com/joelnet/eslint-config-mojiscript which seems to achieve this style with eslint.
If you find a solution that works for you, @onetom, please share it here. We could then add it to the readme and website.
I can confirm that eslint-config-mojiscript
allows for automatic reformatting of the parentheses and some other helpful linting.
npm i -S eslint-config-mojiscript
.eslintrc.js
{
...,
extends: 'mojiscript',
...
}
UPDATE:
The eslint rule that allows for this to be automatically formatted is func-call-spacing. Should be as easy as adding:
.eslintrc.js
{
...,
rules: {
'func-call-spacing': ['error', 'always', { allowNewlines: true }]
},
...
}
Typescript users can make use of the same auto-fix by ditching tslint in favor of eslint as TS has already announced they intend to only support eslint in the future. The setup is here. Then, same rule except '@typescript-eslint/func-call-spacing'
instead of 'func-call-spacing'
.
Also, WebStorm supports an auto-fix through the built-in options Preferences > Editor > Code Style > [Typescript/Javascript] > Spacing > Before Parentheses > Function Call Parentheses
.
Thank you for sharing this information, @RichardForrester.
@RichardForrester did you figure out how to make this rule work with prettier?
@alexandermckay It’s been a while since I messed with this, but from memory you don’t really need the mojiscript to use the function-call-spacing rule, and as far as working with prettier, I usually bind a command to first run prettier and then run eslint auto-fix.
@RichardForrester thank you for the tips! It was actually quite simple to get them to work together. I have created a repo which shows how to set this up for anyone else using a combination of prettier
, eslint
and VSCode.
Sanctuary is defined in part by what it does not support. We have done a good job of managing complexity and entropy, and we must continue to do so if Sanctuary is to live a long, healthy life.
Ramda-style currying—the ability to write both
f(x)(y)
andf(x, y)
—is a source of complexity. I've seen this complexity as necessary to prevent code written with Sanctuary from looking strange to newcomers, which would limit the library's initial appeal and thus limit the library's adoption.Last night it occurred to me that we could possibly solve (or at least mitigate) the "
)(
" problem by tweaking the way in which we format function applications.The "
)(
" problemIn JavaScript, this reads very naturally:
This, on the other hand, seems unnatural:
A day ago my impression was that the only aesthetic problem was having opening parens follow closing parens. I now see a second aesthetic problem, as I hope this example demonstrates:
There's no space. There's a significant difference visually between
x)(y
andx, y
. The nesting of subexpressions above is not immediately clear to a human reader. When we include space between arguments—as is common practice in JavaScript—the nesting is clear:This clarity is the primary benefit of Ramda-style currying. I consider
S.concat(x)(y)
bad style not because of the)(
but because if used consistently this style results in expressions which are less clear than their more spacious equivalents.It's worth noting that multiline function applications are also natural with the comma style:
x
,y
, andz
are obviously placeholders for longer expressions in this case.Here's the
)(
-style equivalent:My concern is that visually the
x
is more tightly bound tof
than it is toy
andz
, making the first argument feel privileged in some way.Learning from Haskell
Sanctuary brings many good ideas from Haskell to JavaScript. Perhaps most important is the combination of curried functions and partial application. We might be able to learn from Haskell's approach to function application.
In Haskell, function application is considered so important that a space is all it requires syntactically:
f x
in Haskell is equivalent tof(x)
in JavaScript. The associativity of function application is such thatf x y
is equivalent to(f x) y
, which is to say that what we write asf(x)(y)
in JavaScript could simply be writtenf x y
in Haskell.Let's consider how the previous examples would look in Haskell:
All three Haskell expressions are less noisy than both of their JavaScript equivalents. Note that in the second expression it's necessary to use parens. We'll return to this idea shortly.
A small change can make a big difference
The proposal:
When applying a function, include a space before the opening paren.
This means we'd write
f (x)
rather thanf(x)
, andf (x) (y)
rather thanf(x)(y)
. This gives expressions breathing room they lack when formatted in the)(
style.Let's revisit the examples from earlier to see the formatting tweak in action.
This looks odd to me now, but I think it could become natural. The key is to see the spaces as the indicators of function application (as in Haskell) and the parens merely as grouping syntax for the subexpressions. It's interesting to note that the code above is valid Haskell.
Again, this is valid Haskell with "unnecessary" grouping around
x
,y
, andz
. The spaces make it easier for me to determine thatf
is being applied to two arguments (one at a time). This would be even clearer if the arguments were written on separate lines:One could even go a step further:
This leads quite naturally to the original multiline example:
The space is advantageous in this case too, separating
x
fromf
sox
binds more tightly, visually, with the other arguments than with the function identifier.Realistic example
Here's a function from sanctuary-site, as currently written:
Here's the function rewritten using the proposed convention:
Here's a Lispy alternative which makes the nesting clearer:
I like the comma style best, although I can imagine growing to like the proposed convention. Even if we decide that the proposed convention makes code slightly less easy to read we should consider adopting it in order to reap the benefits outlined below.
Benefits of replacing Ramda-style currying with regular currying
Although this proposal is focused on an optional formatting convention, it is motivated by the desire to simplify. If we decide that the proposed convention addresses the readability problems associated with
)(
style, we can replace Ramda-style currying with regular currying. This would have several benefits:Simpler mental model. When learning Sanctuary or teaching it to others one would not need to read or explain the interchangeability of
f(x)(y)
andf(x, y)
for Sanctuary functions.One and only one. There would be a single way to express function application (the Haskell way). When writing code one would no longer be distracted by wondering whether
f(x, y)
is more efficient thanf(x)(y)
. Teams would not need to choose one style or the other (although there may still bef(x)
versusf (x)
debates).Agreement between code examples and type signatures. Our type signatures indicate that Sanctuary functions take their arguments one at a time, but our examples currently use comma style which could be leading readers to believe that our type signatures are inaccurate.
Simpler implementation. The currying code in sanctuary-def would become significantly simpler if it only needed to account for
f(x)(y)(z)
.Poll
I'd love to know where you stand on this.
f(x)(y)
orf (x) (y)
exclusively.f(x, y)
but this proposal has encouraged me to adoptf(x)(y)
orf (x) (y)
.f(x, y)
but find the arguments for dropping Ramda-style currying compelling. I would adoptf(x)(y)
orf (x) (y)
if necessary.f(x, y)
and want Sanctuary to continue to use Ramda-style currying.Feel free to vote based on your first impressions but to change your vote if you change your mind.