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 <[email protected]>
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 <[email protected]>
>> 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 [email protected] 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 [email protected]:
>>>>
>>>>> 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 <[email protected]>
>>>>> 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 [email protected]
>>>>> 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 [email protected].
>>>>> 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 [email protected].
>>>
>> 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 [email protected].
> 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 the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4K01hBRkjLaRPj5ktViNNjYqdFbKdysvFcDVG%3DgBp78dA%40mail.gmail.com.

Reply via email to