That’s what I meant. The reduce solution requires the reverse. The state proposal is very close to the Enum.map_reduce/3 solution, as it keeps all of its noise in passing the accumulator, matching on it, and returning it.
I am sure it is clear for an Elixir developer but so is Enum.map_reduce/3. Can we find a point midway? Maybe yes, maybe no. :) PS: And, although we got used with for+:reduce, we have to admit that the fact the accumulator is matched in a clause inside do-end is a specific behavior to for that we partly accept because we got used to it! On Fri, Dec 17, 2021 at 19:19 Zach Daniel <zachary.s.dan...@gmail.com> wrote: > The first approach I described was just me confirming my understanding of > a corollary of the desired behavior that we can do now. The original > problem stated using the method I showed in my second example would look > like this, which seems pretty clean to me. > > ``` > section_counter = 1 > lesson_counter = 1 > > {sections, _} = > for section <- sections, > state: %{section_counter: section_counter, lesson_counter: > lesson_counter} do > %{lesson_counter: lesson_counter, section_counter: section_counter} -> > lesson_counter = if section["reset_lesson_position"], do: 1, else: > lesson_counter > > {lessons, lesson_counter} = > for lesson <- section["lessons"], state: lesson_counter do > lesson_counter -> > lesson = Map.put(lesson, "position", lesson_counter) > {lesson, lesson_counter + 1} > end > > section = > section > |> Map.put("lessons", lessons) > |> Map.put("position", section_counter) > > {section, %{section_counter: section_counter + 1, lesson_counter: > lesson_counter}} > end > ``` > Although I did originally forget to include the fact that if `state` is > specified then not only do you match on it and return a tuple, but that the > comprehension would return a tuple as well. > > Sent via Superhuman <https://sprh.mn/?vip=zachary.s.dan...@gmail.com> > > > On Fri, Dec 17, 2021 at 1:04 PM, José Valim <jose.va...@dashbit.co> wrote: > >> Hi Zach, >> >> I recommend trying out your approach on the original problem. You will >> see that you will need to accumulate elements, add Enum.reverse/1, etc. All >> which will make the solution noisier. >> >> On Fri, Dec 17, 2021 at 6:59 PM Zach Daniel <zachary.s.dan...@gmail.com> >> wrote: >> >>> I may not fully be understanding what we're looking for here, but it >>> seems like this would effectively be the equivalent of: >>> >>> ``` >>> result = >>> for foo ← [1, 2, 3], reduce: %{acc: [], counter: 0} do >>> %{acc: acc, counter: counter} -> >>> new_acc = some_calc(acc) >>> %{acc: new_acc, counter: counter + 1} >>> end >>> >>> actual_result = result.acc >>> ``` >>> >>> I'm wondering if we could just introduce an additional `state` that you >>> match on, and return as a tuple from the for loop body? I think this is >>> similar to what the original proposal wanted, but it involves only knowing >>> that if you use the `state` option, you need to return a tuple of the for >>> loop result and the new state. And it looks similar to a genserver in that >>> regard, which makes it feel reasonably conventional. >>> >>> ``` >>> result = >>> for foo ← [1, 2, 3], reduce: [], state: %{counter: 0} do >>> acc, state → >>> {some_calc(acc, state.counter), %{state | counter: state.counter + >>> 1}} >>> end >>> ``` >>> >>> >>> Sent via Superhuman <https://sprh.mn/?vip=zachary.s.dan...@gmail.com> >>> >>> >>> On Fri, Dec 17, 2021 at 10:35 AM, João Pedro Evangelista < >>> evangelistajo...@gmail.com> wrote: >>> >>>> *> In fact, making special-semantics different syntactically to be more >>>> googleable * >>>> >>>> Also more easily scannable while reading the code, we would know that >>>> this variable has more meaning among the other ones >>>> On Friday, December 17, 2021 at 9:32:18 AM UTC-3 christ...@gmail.com >>>> wrote: >>>> >>>>> *> I did consider introducing (precisely) $ for variables but my >>>>> concern is that, by introducing special syntax, I believe most would >>>>> expect >>>>> it to be fully mutable, so you can modify it from any scope.* >>>>> >>>>> I am not sure if I can envision a way to allow imperative-ish >>>>> variables without introducing special semantics. So I feel like supporting >>>>> the new semantics with special syntax would allow us to set correct >>>>> expectations about its scope and mutability when introducing/documenting >>>>> it! >>>>> >>>>> In fact, making special-semantics different syntactically to be more >>>>> googleable is a perk over plain variables in my mind. For example, >>>>> searching *"ruby double at"* (a comparatively oblique ruby language >>>>> identifier feature, *@@class_variables*), returns an appropriate top >>>>> result >>>>> <https://stackoverflow.com/questions/5890118/what-does-variable-mean-in-ruby> >>>>> (from an incognito browser session, no less)! So maybe an *"elixir >>>>> dollar variable" * google search is a reasonable standard to hold >>>>> ourselves to. >>>>> >>>>> On Friday, December 17, 2021 at 5:40:14 AM UTC-5 sabi...@gmail.com >>>>> wrote: >>>>> >>>>>> Indeed this doesn't address the issue of the level of nesting, and is >>>>>> confusing in this case. >>>>>> >>>>>> The new syntax could maybe include the level of nesting information >>>>>> somehow, e,g. `* $section_counter*` in the parent loop, `* >>>>>> $$section_counter*` in the child loop...? >>>>>> Or *$1.section_counter = 1 *(parent),* $2.section_counter = 1 * >>>>>> (child)? (slightly inspired by &1) >>>>>> *.* >>>>>> >>>>>> Another way to deal with this inconsistency could be to forbid nested >>>>>> comprehension with variables, and require to extract as a new function >>>>>> (in >>>>>> the same way the & cannot be nested and require to use fn). >>>>>> Most examples would probably be easier to understand this way >>>>>> anyway, but this might limit the power of the feature. >>>>>> >>>>>> Or maybe just having the compiler raising an error if trying to >>>>>> re-assign within a nested block, with a helpful beginner-friendly >>>>>> message, >>>>>> could be enough to clear this confusion? >>>>>> I think this is not so much harder to figure than the fact than a >>>>>> re-assignment within an *if* doesn't work as in imperative languages. >>>>>> >>>>>> By looking at the examples here, I feel that the last one might be >>>>>> the most elegant of these 3 ideas: >>>>>> https://gist.github.com/sabiwara/97c480c2076666ba9b98cf7a142a5a0f >>>>>> >>>>>> >>>>>> Le ven. 17 déc. 2021 à 16:14, José Valim <jose....@dashbit.co> a >>>>>> écrit : >>>>>> >>>>>>> *Re: for section <- sections, $section_counter = 1, $lesson_counter >>>>>>> = 1 do* >>>>>>> >>>>>>> I did consider introducing (precisely) $ for variables but my >>>>>>> concern is that, by introducing special syntax, I believe most would >>>>>>> expect >>>>>>> it to be fully mutable, so you can modify it from any scope. That's >>>>>>> why I decided to go with plain variables, because they already have a >>>>>>> limited scope in Elixir and clear rules (but at the same time I agree >>>>>>> that >>>>>>> adding :let would make those clear rules precisely more confusing!). >>>>>>> >>>>>>> On Fri, Dec 17, 2021 at 7:01 AM Christopher Keele < >>>>>>> christ...@gmail.com> wrote: >>>>>>> >>>>>>>> I love the thought put into this proposal, and really like the >>>>>>>> problem it is tackling! I am looking forward to the next proposal and >>>>>>>> will >>>>>>>> try to get to my inbox earlier for it. >>>>>>>> >>>>>>>> *Proposal Feedback* >>>>>>>> >>>>>>>> I mostly second the impressions voiced here, but *really* want to >>>>>>>> call attention to the criticism: >>>>>>>> >>>>>>>> *> this breaks refactoring for the inner contents of `for`* >>>>>>>> >>>>>>>> This is the real true deal-breaker for me. Referential transparency >>>>>>>> is a huge part of my mental model of Elixir and the key reason why it >>>>>>>> is >>>>>>>> such a joy to maintain code in. I am not sure if it is possible to >>>>>>>> introduce an imperative-loop construct that *doesn't* violate this >>>>>>>> property, so I may have to get over that. I do remember how painful it >>>>>>>> was >>>>>>>> to remove assignment-inside-ifs, though. >>>>>>>> >>>>>>>> *Replies* >>>>>>>> >>>>>>>> *Re: **for section <- sections, $section_counter = 1, >>>>>>>> $lesson_counter = 1 do* >>>>>>>> >>>>>>>> *> Maybe a possibility could be to distinguish comprehension >>>>>>>> variables, for example by prefixing them in the same way as module >>>>>>>> attributes are prefixed with `@`.* >>>>>>>> >>>>>>>> This does elegantly solve my refactoring concern; in that >>>>>>>> "imperative" comprehension variables copied out of the comprehension >>>>>>>> could >>>>>>>> immediately raise a syntax error, as would moving them into a different >>>>>>>> comprehension that does not have them declared as imperative in the >>>>>>>> comprehension head. The compiler would also have to enforce never >>>>>>>> letting >>>>>>>> you use the same name with an imperative variables as with a normal >>>>>>>> one, to >>>>>>>> completely eliminate edge cases. I think this solution even works for >>>>>>>> nested comprehensions, though I still am not sure how *that* would >>>>>>>> work with the existing proposal. >>>>>>>> >>>>>>>> *> We could maybe even remove the `let` keyword altogether?* >>>>>>>> >>>>>>>> *That * makes me really like syntax. We are not exactly running >>>>>>>> short on propositions but it nice to keep that overhead low. Also, the >>>>>>>> only >>>>>>>> other existing identifier syntax (module attributes) use a prefix/sigil >>>>>>>> approach as well, and this feels in the same category to me: we are >>>>>>>> introducing a different type of identifier with different scoping rules >>>>>>>> (even though what happens at compile time to it is wildly different). >>>>>>>> >>>>>>>> *Re: **overloading the **<- operator* >>>>>>>> >>>>>>>> *> My concern about this is that `<-` in for means extracting >>>>>>>> something from the collection, so giving it another meaning inside an >>>>>>>> option can be quite confusing.* >>>>>>>> >>>>>>>> *> If I'm not mistaken it actually means pulling the next item from >>>>>>>> an enumerable.* >>>>>>>> >>>>>>>> FWIW I've been writing Elixir for years and I still forget when I >>>>>>>> crack open a *for* or for a *with* that I need to be using *<-* . >>>>>>>> I've just internalized it as the "powerful SpecialForms clause >>>>>>>> operator". >>>>>>>> So I don't think allowing its use in other powerful new constructs, >>>>>>>> potentially nested in *for* or *with*, or inside their options >>>>>>>> lists, would be confusing, from my perspective at least. >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> -- >>>>>>>> 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/72ee4929-efde-476e-9124-bacd7460c486n%40googlegroups.com >>>>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/72ee4929-efde-476e-9124-bacd7460c486n%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/CAGnRm4%2BsbvBxoj1mECXzBna%3DJE-R8%2Bj-CBuRZvgAf%2BsLp2aMjw%40mail.gmail.com >>>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2BsbvBxoj1mECXzBna%3DJE-R8%2Bj-CBuRZvgAf%2BsLp2aMjw%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/10d18915-55da-4b52-8e12-0992625039e3n%40googlegroups.com >>>> <https://groups.google.com/d/msgid/elixir-lang-core/10d18915-55da-4b52-8e12-0992625039e3n%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/kxaott2e.e380bedb-a7bd-4f10-8d2c-573d453c6880%40we.are.superhuman.com >>> <https://groups.google.com/d/msgid/elixir-lang-core/kxaott2e.e380bedb-a7bd-4f10-8d2c-573d453c6880%40we.are.superhuman.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/CAGnRm4KYBajtuG0sAA%2BOtaWDo8AKn5csz6irK3f-Y1BLmrqvbw%40mail.gmail.com >> <https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4KYBajtuG0sAA%2BOtaWDo8AKn5csz6irK3f-Y1BLmrqvbw%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/kxaprh1k.8bfd20dc-9ccf-46d7-b01a-9746ae11d488%40we.are.superhuman.com > <https://groups.google.com/d/msgid/elixir-lang-core/kxaprh1k.8bfd20dc-9ccf-46d7-b01a-9746ae11d488%40we.are.superhuman.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/CAGnRm4KYCQC%2BYx8ibOW4Gbnbj7161BvvkbDSdE9oaV_zkUYhLw%40mail.gmail.com.