Just a nit-pick: looks like the respective EEP has not been merged?
ā¦ and people accuse me of giving them bad ideasā¦
This just shows how different minds work - I honestly belive that is a nice and compact syntax, using it all the time
Indeedā¦ I would call it a bug exploited as a feature
ā¦ and people who deliberately use it that way should be flogged (just kidding).
Butā¦ honestly? Like, for real?
I could imagine an even more novel way of writing comprehensions. Something like this for example:
let
%% Various types of generators (all strict)
[Elem] <- ListExpression,
<<Bin>> <- BinExpression,
#{K := V} <- MapExpression,
{T} <- TupleExpression, %% Long live EEP 12!
%% Zip generators from EEP 73
[A] <- As && [B] <- Bs,
%% You can put here ordinary Erlang code
X = A + B,
case T of
foo -> io:put_chars("hello world!\n");
bar -> whatever()
end,
%% ?= is the only way to filter
true ?= V > 0, % used as a traditional, boolean filter (without guard semantics!)
{ok, Val} ?= Elem % but any pattern matching is allowed
in
[Val] % you could also write <<Val>>, #{Key => Val} or {Val}
end % I guess it would be possible to parse the let expression without the end, but it's more uniform this way
Maybe with an additional rule that the comprehension must begin with a generator (so no more [X || if_this:is_true()]
).
I just wanted to say that I find this proposition (i.e. letā¦inā¦end
) super nice and friendly. Itās my favorite style from the ones Iāve seen in this thread so far.
Everyone to his own, I guess, that is probably the worst suggestion Iāve seen in this thread
I really like this:
And it could also be applied to todayās comprehensions if ?=
is introduced: you could use ?=
as a filter (therefore fixing the weird guard semantics we have today) and also soft matching (replacing the <-
and <=
operators).
I am not a biggest fan of the return type being specified in in
, because I need to āsearchā too deep to find if a let
will return a list/map/binary. But you could also mix with my earlier proposal and have it at a glance:
[let X <- List in X * 2 end]
IMO, it reads like a nice way to say this list/map/binary is derived from something else. And it makes it immediately clear you have a comprehension, you donāt need to navigate inside the list to find a ||
.
Constructions like this for example:
AccS1 = [Acc || Acc /= void] ++ AccS,
and:
COpts = [{src_dir, SrcDir} || SrcDir /= <<>>] ++
...
and of course in tests:
[ ct:log("There was a problem with the issue ~p", [Prob]) || Verbose ]
Yes, after the decision was taken, I had to leave to pick up my kid, so the PR to OTP was merged faster than me going home and merging the EEP.
EEP merged now.
I will also see what to do about old EEPs. In a way, we have EEPs that are there lying around and have been accepted to the EEP repo because they are valid and contain great ideas. I do not think it is fair to reject them when they have not been considered to be added to the language because no decision was taken at the time. Not sure if adding a new EEP category for those makes senseā¦ I will think about it
I personally donāt find that a problem. Itās the same thing how I have to read through an entire function to see whatās the return value going to be.
On the other hand, I wanted to keep both generators and the result of the comprehension similar in the sense that the type is determined by the kind of brackets used around the element you generate/yield. It also keeps the special syntax used within binary and map expressions inside a binary or map expression (and without further nesting inside the let ... in ... end
). I think thatās cool.
When I read let
, my brain makes a context switch to Haskell, Elm or even Basic. In my perception, you use let
to create local variables and/or functions. To use let as here specifically to represent comprehensions does not make sense to me. Then in instead of let
use a keyword like itterate
? And if you wanted to introduce something like a new expression such as let
into core Erlang, why not just add it to the standard libraries?
That was partially my thought too, which is another reason why I suggested to at least move the let
inside comprehension, so there are some differences to how let
s are used almost everywhere else. As far as I know, languages like Haskell and Elm do not allow let inside data constructors (not even JavaScript does).
I would strongly suggest against let ... in ...
(with or without []
around it), for the reasons @schnef gave, namely, that let
is a keyword used to introduce/define/declare new variables (in all languages that have a let
keyword? MLs/Haskell, Lisps/Schemes, JavaScript, Rust, ā¦), which would thus be contrary to all other already well-established ātraditionā for no apparent good reason, making Erlang an even more esoteric language than it already is.
I would also strongly suggest against using for ... of ... end
(with or without []
around it) as it looks a lot like an imperative construct ā itās not clear to me that the last expression of the body is āreturnedā ā isnāt it just a for-each? ā and I would bet anyone coming from other languages, even functional ones, would agree here (a survey to confirm this would be good, but I doubt itād be feasible).
Instead I would suggest simply replacing ||
with of
for the āgood behaviorā (fixing the existing problems with list comprehensions), keeping ||
for the current behavior for backwards compatibility. The advanges I see: (1) doesnāt introduce new reserved keywords; (2) same number of characters; (3) easy to change between old and new behavior; (4) anyone who generally understands the list comprehension syntax will generally understand both.
[X || X <- List]
% vs
[X of X <- List]
If thereās more inertia behind new syntax to the likes of let-in
or for-of-end
, that puts generated expression after generating expressions, I would recommend reading SRFI 42 for inspiration, as I think itās a good syntax done well. Although, of course, it wouldnāt be possible import all the juicy parentheses into Erlangā¦
Just to clarify: I donāt have strong feelings pro/contra let
. I just picked that one in my makeshift example because itās already a reserved word, plus I felt that this proposed use is actually about introducing variables (those that the generators generate). But I understand itās not the typical use of let
.
To be honest, your proposal of using [... of ...]
is not bad, but not my favorite either for two reasons:
- I find putting the elements you yield at the end instead of upfront is the more natural reading order.
- I donāt see how āofā would fit into this construct? I mean what would be the plain English sentence that describes what the comprehension does and contains a prominent āofā?
If you want to replace ||
you could just introduce any new operator (built up from symbols) instead of a keyword. Itās easy to pick an operator that is currently invalid syntax: :-)
, @@
, <|
, |||
, ~|
or whatever.
But Iād rather add a new reserved word: maybe
expressions showed us that itās possible and how could one approach the problem. Then we could have generate ... in ... end
, from ... build ... end
or something similar that most people could accept.
Fair point. I think of it as āsuch thatā or āof the generator [expression]ā.
[foo(X) of X <- List]
would read ācreate a list with foo(X)
elements of
the generator [expression] X <- List
ā.
Itās not that clear-cut. I read ||
as āsuch thatā (similar to plain |
in various mathematical constructions) and itās pretty natural to me.
My point being that itās just what oneās used to.
@bjorng do you think all currently relaxed generators in the OTP codebase should be replaced with strict ones (where possible, of course)? Is there any difference in performance by using one over the other?
(Disclaimer: I still donāt like this )
I have since seen it used in a few places in the OTP codebase, for example here in zip
or here in dets_utils
. Another variation of the theme can be found here in dets
(the generator part could be dropped I think).
So I wonderā¦ what does the OTP team (@bjorng?) have to say about this style (if you want to call it such)? Is this an accepted or even encouraged way of doing things?