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.

Reply via email to