csadewa
Hi all, recently i have been trying out for comprehension after reading Introducing `for let` and `for reduce` and realizing i am also have severly underutilized for. Just now i have spend quite sometime debugging code written with for only to realize there is the following: there’s no “only” binding operation, it always also get used with filters.
For example, consider following code:
data = [%{a: 1, b: 2}, %{a: 2}]
for datum <- data,
b = datum[:b],
b == nil do
datum
end
what would you expect the result be? [%{a: 2}] like i was? the actual result (on elixir 1.10.3) was []. b = datum[:b] is actually treated as filter, and elixir recognize nil and false as falsey value.
I think this is pretty strange behaviour, though maybe this is the implication on A comprehension is made of three parts: generators, filters, and collectables. from https://elixir-lang.org/getting-started/comprehensions.html ?
For context, even at the web tutorial we find following code, in which path = Path.join(dir, file) is treated as variable assignment.
dirs = ['/home/mikey', '/home/james']
for dir <- dirs,
file <- File.ls!(dir),
path = Path.join(dir, file),
File.regular?(path) do
File.stat!(path).size
end
and at Haskell, they have separation between declaration and filter, like in following
Trending in Discussions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 9- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
LostKobrakai
That‘s how it‘s supposed to work. Assignments evaluate to the value of the left hand side (
left == (a = left)). If that value is falsy the current generated value is filtered.That doesn‘t mean you cannot use assignments, but you need to be careful with ones, which can return falsy values.
paulstatezny
Given the original comment and @LostKobrakai 's response, I’m thinking something like this would achieve what you want:
(Modifying the original code snippet.)
csadewa
I understand that
b = datum[:b]is an expression and it would return the value ofdatum[:b], what i want to highlight is that assignment is common, and having it treated always as filter result in really suprising gotcha, and you would need to be really careful everywhere as faulty value likenilandfalseis commonly used (ex: Ecto get_entity function has possibility return nil, Map.get default to nil, access pattern [:b] default to nil).That’s true, wrapping it inside
{:ok, }tuple does solve the issue, but in spirit of idiomatic code, it looks like more noisy compared toin spirit of idiomatic code, wouldn’t it better to make
fortreatb = datum[:b]as purely assignment operator? though i understand it probably will create breaking change. alternatively, maybe making a macroletwhich would transformlet(b = datum[:b])to{:ok, b} = {:ok, datum[:b]}would be more feasible?example if there’s let
csadewa
Just realize it, but probably only
{}would work too, still thinking havingletwould be better though:or
josevalim
In your case you could also do:
csadewa
thanks for pointing that out!
what do you think about having an assignment syntax in
forwhich are not used as filter though? should wrapping assignment in{}be default practice when usingforcomprehension?josevalim
In such cases I also tend to do this:
So that is likely the approach I would go with if it can’t be done with a guard?
csadewa
That’s interesting, thinking assignment as a special case of generator. Should i update elixir tutorial at https://elixir-lang.org/getting-started/comprehensions.html (and maybe other related docs) to this syntax (and maybe add warning about using
=infor?)josevalim
I don’t think it is worth doing this change in general, because it can be nil in many cases and sometimes you want to discard nil values, but we probably need to add a note to the API documentation just in case.