That's fair enough! Though from my perspective both for! and strict: true would be about equally far from the <- where matches fail. But I can see the keyword format getting lost in the filters and other keywords.
On Thu, Jun 10, 2021 at 3:14 PM José Valim <jose.va...@dashbit.co> wrote: > Sorry, I meant to someone reading the code. The strict option is modifying > the behavior of the operator <-, which may be quite before it in the text. > > I prefer for! in this case as it is upfront. > > On Fri, Jun 11, 2021 at 00:09 Christopher Keele <christheke...@gmail.com> > wrote: > >> > My concern with :strict is that it changes the behavior considerably of >> the generators but it may show up only quite later on, far from them, >> especially if you have multiple filters. >> >> Could you elaborate? I don't quite think I understand, particularly *"[the >> behaviour] may show up only quite later on"* >> >> Does "quite later" here refer to code distance (the MatchError's >> stacktrace would point away from/bury the for location)? Or temporal >> distance? >> >> On Thursday, June 10, 2021 at 2:58:03 PM UTC-7 José Valim wrote: >> >>> My concern with :strict is that it changes the behavior considerably of >>> the generators but it may show up only quite later on, far from them, >>> especially if you have multiple filters. >>> >>> On Thu, Jun 10, 2021 at 23:56 Christopher Keele <christ...@gmail.com> >>> wrote: >>> >>>> > for {:ok, num} <- list, strict: true, do: num >>>> >>>> Agreed, this is more or less exactly what I was pitching. >>>> >>>> On Wednesday, June 9, 2021 at 10:16:25 PM UTC-7 tal...@gmail.com wrote: >>>> >>>>> I would like to add a solution within the existing language: >>>>> >>>>> >>>>> ```elixir >>>>> > list = [{:ok, 1}, {:ok, 2}, {:error, :fail}, {:ok, 4}] >>>>> > for el <- list, do: ({:ok, num} = el; num) >>>>> ** (MatchError) no match of right hand side value: {:error, :fail} >>>>> ``` >>>>> I think this is reasonable. >>>>> >>>>> Acctually the built in filtering in `for` caught me off guard, I was >>>>> expecting for to fail unless all elements matched. So for me the better >>>>> solution would be to always make matching in `for` strict. But I guess >>>>> this >>>>> is too late now for backwards compatibility. Another alternative to `for!` >>>>> would be: >>>>> >>>>> ```elixir >>>>> > list = [{:ok, 1}, {:ok, 2}, {:error, :fail}, {:ok, 4}] >>>>> > for {:ok, num} <- list, strict: true, do: num >>>>> ** (MatchError) no match of right hand side value: {:error, :fail} >>>>> ``` >>>>> >>>>> I don't like the use of the exclamation mark in `for!` because it has >>>>> little meaning relative to the existing use of the exclamation mark in >>>>> Elixir. >>>>> >>>>> onsdag 9. juni 2021 kl. 13:17:04 UTC+2 skrev ad...@a-corp.co.uk: >>>>> >>>>>> I also love the proposal. >>>>>> >>>>>> It's a shame we can't re-use the `with` semantics of `=` raising a >>>>>> match error in the for. >>>>>> >>>>>> My two cents is `for!` makes the most sense, and follows the >>>>>> conventions of other functions. >>>>>> >>>>>> Best >>>>>> >>>>>> Adam >>>>>> >>>>>> On 8 Jun 2021, at 18:18, Christopher Keele <christ...@gmail.com> >>>>>> wrote: >>>>>> >>>>>> This feature would be very useful, I've experience this >>>>>> signature-change pain point before too (and kind of have been avoiding >>>>>> `for` ever since, TBH). >>>>>> >>>>>> I'm reluctant to increase the surface area of the language itself, >>>>>> what do you think about adding a `:strict` option to `for` instead of a >>>>>> new >>>>>> special form/kernel macro/operator? >>>>>> >>>>>> On Monday, June 7, 2021 at 9:50:45 AM UTC-7 eric.meado...@gmail.com >>>>>> wrote: >>>>>> >>>>>>> ## Background >>>>>>> >>>>>>> `for` comprehensions are one of the most powerful features in >>>>>>> Elixir. It supports both enumerable and bitstring generators, filters >>>>>>> through boolean expressions and pattern matching, collectibles with >>>>>>> `:into` >>>>>>> and folding with `:reduce`. >>>>>>> >>>>>>> One of the features are automatic filtering by patterns in >>>>>>> generators: >>>>>>> >>>>>>> ```elixir >>>>>>> list = [{:ok, 1}, {:ok, 2}, {:error, :fail}, {:ok, 4}] >>>>>>> for {:ok, num} <- list, do: num >>>>>>> #=> [1, 2, 4] >>>>>>> ``` >>>>>>> >>>>>>> Generator filtering is very powerful because it allows you to >>>>>>> succinctly filter out data that is not relevant to the comprehension in >>>>>>> the >>>>>>> same expression that you are generating elements out of your >>>>>>> enumerable/bitstrings. But the implicit filtering can be dangerous >>>>>>> because >>>>>>> changes in the shape of the data will silently be removed which can >>>>>>> cause >>>>>>> hard to catch bugs. >>>>>>> >>>>>>> The following example can show how this can be an issue when testing >>>>>>> `Posts.create/0`. If a change causes the function to start returning >>>>>>> `{:ok, >>>>>>> %Post{}}` instead of the expected `%Post{}` the test will pass even >>>>>>> though >>>>>>> we have a bug. >>>>>>> >>>>>>> ```elixir >>>>>>> test "create posts" do >>>>>>> posts = Posts.create() >>>>>>> for %Post{id: id} <- posts, do: assert is_integer(id) >>>>>>> end >>>>>>> ``` >>>>>>> >>>>>>> The example uses a test to highlight the issue but it can just as >>>>>>> well happen in production code, specially when refactoring in other >>>>>>> parts >>>>>>> of the code base than the comprehension. >>>>>>> >>>>>>> Elixir is a dynamically typed language but dynamic typing errors are >>>>>>> less of an issue compared to many other dynamic languages because we are >>>>>>> usual strict in the data we accept by using pattern matching and guard >>>>>>> functions. `for` is by design not strict on the shape of data it accepts >>>>>>> and therefor loses the nice property of early failure on incorrect data. >>>>>>> >>>>>>> ## Proposal >>>>>>> >>>>>>> I propose an alternative comprehension macro called `for!` that has >>>>>>> the same functionality as `for` but instead of filtering on patterns in >>>>>>> generators it will raise a `MatchError`. >>>>>>> >>>>>>> ```elixir >>>>>>> posts = [{:ok, %Post{}}] >>>>>>> for! %Post{id: id} <- posts, do: assert is_integer(id) >>>>>>> #=> ** (MatchError) no match of right hand side value: {:ok, %Post{}} >>>>>>> ``` >>>>>>> >>>>>>> Pattern matching when not generating values with `=` remains >>>>>>> unchanged. >>>>>>> >>>>>>> `for!` gives the developer an option to be strict on the data it >>>>>>> accepts instead of silently ignoring data that does not match. >>>>>>> >>>>>>> ## Other considerations >>>>>>> >>>>>>> You can get strict matching with `for` today by first assigning to a >>>>>>> variable. This way you can also mix filtering and strict matching >>>>>>> patterns. >>>>>>> >>>>>>> ```elixir >>>>>>> posts = [{:ok, %Post{}}] >>>>>>> for post <- posts, >>>>>>> %Post{id: id} = post, >>>>>>> do: assert is_integer(id) >>>>>>> #=> ** (MatchError) no match of right hand side value: {:ok, %Post{}} >>>>>>> ``` >>>>>>> >>>>>>> Another alternative is to introduce a new operator such as `<<-` >>>>>>> (the actual token can be anything, `<<-` is only used as an example) for >>>>>>> raising pattern matches instead of introducing a completely new macro. >>>>>>> >>>>>>> ```elixir >>>>>>> posts = [{:ok, %Post{}}] >>>>>>> for %Post{id: id} <<- posts, do: assert is_integer(id) >>>>>>> #=> ** (MatchError) no match of right hand side value: {:ok, %Post{}} >>>>>>> ``` >>>>>>> >>>>>>> A downside of adding new functions or macros is that it doesn't >>>>>>> compose as well compared to adding options (or operators) to existing >>>>>>> functions. If we want to add another variant of comprehensions in the >>>>>>> future we might be in the position that we need 4 macros, and then 8 >>>>>>> and so >>>>>>> on. >>>>>>> >>>>>>> Another benefit of adding an operator is that you can mix both `<-` >>>>>>> and `<<-` in a single comprehension. >>>>>>> >>>>>>> The downside of an operator is that it adds more complexity for the >>>>>>> language user. We would also need an operator that is visually close to >>>>>>> `<-` but still distinctive enough that they are easy to separate since >>>>>>> their behavior are very difference. >>>>>>> >>>>>> >>>>>> -- >>>>>> You received this message because you are subscribed to the Google >>>>>> Groups "elixir-lang-core" group. >>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>> send an email to elixir-lang-co...@googlegroups.com. >>>>>> To view this discussion on the web visit >>>>>> https://groups.google.com/d/msgid/elixir-lang-core/42adcfba-12d8-4469-a156-f412b0d290a9n%40googlegroups.com >>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/42adcfba-12d8-4469-a156-f412b0d290a9n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>> . >>>>>> >>>>>> >>>>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "elixir-lang-core" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to elixir-lang-co...@googlegroups.com. >>>> >>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/elixir-lang-core/f4d5c0be-567a-4a7d-9b39-68202226c788n%40googlegroups.com >>>> <https://groups.google.com/d/msgid/elixir-lang-core/f4d5c0be-567a-4a7d-9b39-68202226c788n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> -- >> You received this message because you are subscribed to the Google Groups >> "elixir-lang-core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to elixir-lang-core+unsubscr...@googlegroups.com. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/elixir-lang-core/0ce03abc-61bb-4423-b6a8-704d1d62169fn%40googlegroups.com >> <https://groups.google.com/d/msgid/elixir-lang-core/0ce03abc-61bb-4423-b6a8-704d1d62169fn%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > -- > You received this message because you are subscribed to a topic in the > Google Groups "elixir-lang-core" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/elixir-lang-core/LEUD2alHPiE/unsubscribe > . > To unsubscribe from this group and all its topics, send an email to > elixir-lang-core+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4K01hBRkjLaRPj5ktViNNjYqdFbKdysvFcDVG%3DgBp78dA%40mail.gmail.com > <https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4K01hBRkjLaRPj5ktViNNjYqdFbKdysvFcDVG%3DgBp78dA%40mail.gmail.com?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "elixir-lang-core" group. To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAD9kT2QPn_prFiS%2BR9eemqA43DMvvOB8NrAweL2PgE_ZR2g6Cg%40mail.gmail.com.