There is plenty of room for debate over this advice. Prefer do notation if the effects of the flow are more important than the data structure, and prefer using Applicative style if the data structure layout is more important to understand than the effects to build it. In the example in the article, the software is easier to understand from the perspective of how data was acquired to populate the record. However, this isn't true in a general sense; often records are purpose built in higher level languages to reflect effects and constrain them.
Example: when using Parsec or similar, Applicative style is far easier to understand when examining how records are parsed. In this case, the records will likely reflect the AST, which in turn will reflect the grammar. The two reinforce each other: additional constraints on syntax will naturally flock to data constructors.
lemonwaterlime 1 days ago [-]
My view is more in line with your statement.
> Prefer do notation if the effects of the flow are more important than the data structure, and prefer using Applicative style if the data structure layout is more important to understand than the effects to build i.
kccqzy 23 hours ago [-]
I personally think of the distinction as whether the name is important. In Applicative style you apply the arguments to the constructor in order. In do notation you give names to the intermediate pieces. If the names are important enough to be stated (which is usually the case for records) use do notation. Otherwise don’t. This really reminds me of languages like Swift that have external names (aka argument label); whether a function parameter deserves an external name should be decided on a case-by-case basis and similarly here.
Garlef 18 hours ago [-]
From an outsiders perspective, this kind of discussion seems... immature?
(As in: Kids discussing something "important" to them while the parents chuckle)
I like the do notation better because it expresses the intent at the point of usage and I don't have to look up the definition when coming back after a few weeks.
kccqzy 10 hours ago [-]
An analogy is that in Python someone declared a function
def foo(bar, baz, womp):
There is a discussion on whether you should call it like
foo(1, 2, 3)
Or like
foo(bar=1, baz=2, womp=3)
Coding style discussions tend to be like this.
throwway262515 13 hours ago [-]
Immature in what way? TFA advocates a certain coding style over a different one.
throwaway17_17 23 hours ago [-]
In the ‘Caveats’ section the author discusses that the advice only applies to types constructed via Record Syntax and that datatypes with positional arguments are exempt from the rule/preference proposed.
I wonder if the argument for the do notation’s usage being better along several metrics can stand generalization. There are sometimes discussions about the merits of named argument to functions and data constructors versus the much more common positional syntax, the arguments made in favor of more ‘broad spectrum’ named arguments reflect the more specific pro’s being argued in TFA.
It would be interesting to see what types of syntax additions, and the accompanying weight of those additions throughout an entire code base, would prove an acceptable trade off for moving away from positional arguments being the default. I almost assume the extra line noise and extra name selection would be the most negative effects, but the most positive effects are harder for me to guess at. But overall, the reaction of devs to more ‘book keeping’ in there code is hardly ever positive.
wazHFsRy 19 hours ago [-]
In the authors enter first and last name example, isn’t the do notation also much clearer on order of effects? Eg being asked first for first name?
throwway262515 13 hours ago [-]
Applicative operators effect from left to right. So the order of effects in
getPerson :: IO Person
getPerson =
Person
<$> (putStrLn "Enter your first name:" *> getLine)
<*> (putStrLn "Enter your last name:" *> getLine)
is transparently clear (to a seasoned Haskeller).
kccqzy 10 hours ago [-]
That’s only true when you use the IO monad as an Applicative. It’s not true if you use for example the Concurrently type from the async package (which is quite wonderful btw). The ApplicativeDo language extension makes it possible for you to use do notation in this case as well.
Do you have a concrete example using Concurrently where the effect semantics is harder to understand--possibly even misleading--with effect combinators than with do notation?
kccqzy 7 hours ago [-]
When you use Concurrently you are implying that you want things to happen concurrently and you don’t care about effect ordering. You leave the effect ordering to the GHC runtime in how it schedules these green threads. Using do notation here works but it is slightly misleading. A seasoned Haskeller would never see do notation and assume anything about the ordering of effects.
Rendered at 01:17:56 GMT+0000 (Coordinated Universal Time) with Vercel.
Example: when using Parsec or similar, Applicative style is far easier to understand when examining how records are parsed. In this case, the records will likely reflect the AST, which in turn will reflect the grammar. The two reinforce each other: additional constraints on syntax will naturally flock to data constructors.
> Prefer do notation if the effects of the flow are more important than the data structure, and prefer using Applicative style if the data structure layout is more important to understand than the effects to build i.
(As in: Kids discussing something "important" to them while the parents chuckle)
I like the do notation better because it expresses the intent at the point of usage and I don't have to look up the definition when coming back after a few weeks.
I wonder if the argument for the do notation’s usage being better along several metrics can stand generalization. There are sometimes discussions about the merits of named argument to functions and data constructors versus the much more common positional syntax, the arguments made in favor of more ‘broad spectrum’ named arguments reflect the more specific pro’s being argued in TFA.
It would be interesting to see what types of syntax additions, and the accompanying weight of those additions throughout an entire code base, would prove an acceptable trade off for moving away from positional arguments being the default. I almost assume the extra line noise and extra name selection would be the most negative effects, but the most positive effects are harder for me to guess at. But overall, the reaction of devs to more ‘book keeping’ in there code is hardly ever positive.
https://hackage-content.haskell.org/package/async-2.2.6/docs...