for! +1

-bt

On Wed, Jun 9, 2021 at 7:17 AM Adam Lancaster <a...@a-corp.co.uk> wrote:

> 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 <christheke...@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-core+unsubscr...@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-core+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/elixir-lang-core/5E4E868D-88E9-4B5E-9A3B-25F330530687%40a-corp.co.uk
> <https://groups.google.com/d/msgid/elixir-lang-core/5E4E868D-88E9-4B5E-9A3B-25F330530687%40a-corp.co.uk?utm_medium=email&utm_source=footer>
> .
>


-- 

Regards,
Bruce Tate
CEO

<https://bowtie.mailbutler.io/tracking/hit/f8218219-d2a8-4de4-9fef-1cdde6e723f6/c7c97460-016e-45fb-a4ab-0a70318c7b97>

Groxio, LLC.
512.799.9366
br...@grox.io
grox.io

-- 
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/CAFXvW-7WkjsDD5w827HRo9aFjL00Y-1hQo8byo5Ka9pP7EGpag%40mail.gmail.com.

Reply via email to