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.