Ah, gotcha! Yeah, I was mistakenly thinking of `then` as just a macro and forgot it still relies on a function capture, my bad!
As for your last point, I think the key word there is `probably`. As I said, this is not for the 99% of the cases where a more expressive solution is better, but for the more uncommon cases where we're forced to write a `maybe_*` variant of a function just to play nice with the pipe operator. See the count of a single case of this pattern being used in github (`maybe_put`), which in my opinion points to a possibility of improving the expressiveness of `then`, even if marginally. Em seg., 5 de ago. de 2024 às 15:47, Austin Ziegler <halosta...@gmail.com> escreveu: > I’d have to find and/or recreate my test cases for this, but `&foo/1` is > functionally equivalent to `fn x -> foo(x) end` and to `apply(__MODULE__, > :foo, [x])` and it seemed to me that any such capture is treated as a > dynamic (runtime resolution) function, acting as a bit of an optimization > barrier (the internal function could be optimally performed, but there's no > way to inline the behaviour of the internal function). > > This could change in the future, but I *think* would require cooperation > from the BEAM as well as improvements to the Elixir compiler (that I am in > no way qualified to discuss). > > Understand that when I say that there was a performance hit, we're still > talking about a *massive* IPS value in either case, but it’s worth noting. > > I probably wouldn't use either a `then_if/3` as previously proposed or > your `then/3` extension, because I think that there are more expressive > ways to do this, and even avoid the use of `then/2`. A large part of this > is because I used to do things like: > > … > |> case do > … > end > |> case do > … > end > > Which I now consider unreadably complex and would rather see function > names over complex `then/2` or `case/2` pipes. > > -a > > On Mon, Aug 5, 2024 at 2:10 PM Caio Oliveira <caio...@gmail.com> wrote: > >> Oh, interesting! But I'd imagine in `apply`s case this happens because it >> needs to figure stuff out at runtime, which shouldn't be the case here, >> right? >> >> Either way, after thinking about it and looking at `then` usage in the >> wild I feel like a simpler `condition` term instead of a predicate might be >> the way to go, so no extra function calls. >> >> Em seg., 5 de ago. de 2024 às 14:04, Austin Ziegler <halosta...@gmail.com> >> escreveu: >> >>> Related to something else that I was working on, I ran some benchmarks >>> on the performance difference between `apply/3`, calling a captured >>> function, and calling a function directly and the latter was much faster (I >>> want to say it was 10–15% faster, but this was months ago) whereas >>> `apply/3` and captured functions were negligibly different between each >>> other. The proposed `then_if/3` would have one or two captured functions >>> running in the pipeline; if it is something that runs frequently, those >>> would add up. >>> >>> When I'm building the sort of pipeline described, I don't typically have >>> a `maybe_add_header` *as part of the pipeline*, but instead have something >>> like >>> >>> … >>> |> add_modified_since(modified_since) >>> |> add_entity_id(entity_id) >>> >>> Those might call `maybe_add_header` inside, or they might just have two >>> (or more) heads that only add the header if `modified_since` and >>> `entity_id` are non-`nil` and non-blank. >>> >>> I have wanted `{Map,Keyword}.put_if(container, value)` more often, >>> personally (but not frequently enough to want to propose them). >>> >>> -a >>> >>> On Mon, Aug 5, 2024 at 10:04 AM Caio Oliveira <caio...@gmail.com> wrote: >>> >>>> Hi Amos! Yeah, creating functions with the clear intent is good and >>>> preferable. What I don't like is having to create these `maybe_` variants >>>> that don't really add any clarity to the code and just serve to wrap a >>>> conditional statement and make it "pipeable". Because of that it doesn't >>>> feel like the current API is serving to guide devs to writing better code, >>>> but just being more verbose. >>>> >>>> Also I second your point of rarely using `then`, but as I said before, >>>> in that 1% case I need it I think it could be a tad more convenient. >>>> >>>> Em seg., 5 de ago. de 2024 às 10:11, Amos King <a...@binarynoggin.com> >>>> escreveu: >>>> >>>>> I rarely use ‘then’ for anything other than reordering arguments for a >>>>> pipeline. I find that I end up having the same code that is in the ‘then’ >>>>> in multiple place and leads me to create a function, with an intention >>>>> revealing name, instead of using ‘then.’ >>>>> >>>>> The new functions lead to more readable and quicker grokking of the >>>>> code on future passes. I’m always trying to code for future me and future >>>>> others to speed up understanding of the code. I don’t find that this >>>>> increases understanding. I think it would lead to more use of it and >>>>> decrease the amount of time it takes future developers to grok the code. >>>>> >>>>> Amos King >>>>> Binary Noggin >>>>> >>>>> On Aug 5, 2024, at 07:48, Caio Oliveira <caio...@gmail.com> wrote: >>>>> >>>>> >>>>> Good idea! I searched through github to find more usecases like that >>>>> to corroborate this change, and here's what I found: >>>>> >>>>> - 972 results for just `|> maybe_put`: >>>>> https://github.com/search?q=language%3AElixir+%22%7C%3E+maybe_put%22&type=code >>>>> - 214 results for `then(&if...`, where the majority's `else` clause >>>>> was just `&1`: >>>>> https://github.com/search?q=language%3AElixir+%22%7C%3E+then%28%22+%26if&type=code >>>>> >>>>> This *feels* statistically relevant, considering the occurrence of >>>>> `|> then` >>>>> <https://github.com/search?q=language%3AElixir+%22%7C%3E+then%22&type=code> >>>>> is ~6.2k. >>>>> >>>>> I also noticed that most of the uses didn't need the predicate to be a >>>>> function (and I think in this cases maybe using `|> case do` is even >>>>> better, and the missing `else` clause won't be a problem here), so maybe >>>>> this could be even simpler by just taking a value as the condition >>>>> instead. >>>>> >>>>> Also I'm ok with the `then_if` implementation, but I personally still >>>>> prefer the `then(..., if: pred)` as it reads better to me, but I >>>>> understand >>>>> it might be too different from the rest of the kernel code. >>>>> >>>>> Em dom., 4 de ago. de 2024 às 00:15, Jean Klingler <sabiw...@gmail.com> >>>>> escreveu: >>>>> >>>>>> Coming back to some of my projects, I indeed found a bunch of similar >>>>>> cases, e.g. maybe_put_assoc, so even if I'm still on the fence I do see >>>>>> your point. >>>>>> >>>>>> Assuming we introduce it, I'd find it more natural API-wise to have >>>>>> the condition first and then the action, which might be why the nested >>>>>> `if` >>>>>> felt more natural to me (consistent with `if`, other conditionals, and >>>>>> also >>>>>> Clojure's cond). >>>>>> >>>>>> |> then_if(fn_ -> modified_since end, &Req.put_header(&1, >>>>>> "If-Modified-Since", modified_since)) >>>>>> >>>>>> >>>>>> Le dim. 4 août 2024 à 11:22, Caio Oliveira <caio...@gmail.com> a >>>>>> écrit : >>>>>> >>>>>>> Yup, that’s what I usually do, but I see this in many places. >>>>>>> `maybe_put_header`, `maybe_prepend`, etc., which makes me think that an >>>>>>> abstraction would be convenient. And yeah, I wrote a `maybe_then` >>>>>>> initially, but thought it was simple and convenient enough to be in the >>>>>>> kernel. >>>>>>> >>>>>>> On Sat, 3 Aug 2024 at 22:42 Jean Klingler <sabiw...@gmail.com> >>>>>>> wrote: >>>>>>> >>>>>>>> Thank you for providing a concrete example. >>>>>>>> >>>>>>>> Also subjective but I find the following more readable >>>>>>>> >>>>>>>> |> then(&if(modified_since, do: Req.put_header(&1, >>>>>>>> "If-Modified-Since", modified_since), else: &1)) >>>>>>>> >>>>>>>> than >>>>>>>> >>>>>>>> |> then(&Req.put_header(&1, "If-Modified-Since", modified_since), >>>>>>>> if: fn_ -> modified_since end) >>>>>>>> >>>>>>>> But perhaps this case might benefit from introducing a private >>>>>>>> function rather than relying on `then`, especially if this is a >>>>>>>> recurrent >>>>>>>> use-case in your module/project: >>>>>>>> >>>>>>>> defp maybe_add_header(req, _key, nil), do: req >>>>>>>> defp maybe_add_header(req, key, value), do: Req.put_header(req, >>>>>>>> key, value) >>>>>>>> >>>>>>>> ... >>>>>>>> |> maybe_add_header("If-Modified-Since", modified_since) >>>>>>>> |> maybe_add_header("X-Entity-Id", entity_id) >>>>>>>> >>>>>>>> It should also be easy define your own then_if/3 macro if you >>>>>>>> really prefer the clojure style. >>>>>>>> >>>>>>>> >>>>>>>> Le dim. 4 août 2024 à 09:59, Caio Oliveira <caio...@gmail.com> a >>>>>>>> écrit : >>>>>>>> >>>>>>>>> True, although personally I find the one-line `if` kind of >>>>>>>>> confusing, especially when I was newer to the language. >>>>>>>>> >>>>>>>>> > Additionally, your suggestion only implies what happens if >>>>>>>>> `pred` is falsy while mine is clear. >>>>>>>>> >>>>>>>>> This almost convinced me, to be honest! But on the flip side this >>>>>>>>> makes it possible for you to forget to add a `else` clause, and just >>>>>>>>> end up >>>>>>>>> with a `nil` that is potentially hard to find where it came from. >>>>>>>>> >>>>>>>>> Jose replied this in the PR (including here to centralize the >>>>>>>>> discussion and not spam the PR and his email there): >>>>>>>>> >>>>>>>>> > I personally would prefer to write the original code or use no >>>>>>>>> pipeline at all. I don’t think the gain in conciseness justifies the >>>>>>>>> loss >>>>>>>>> in readability. >>>>>>>>> >>>>>>>>> I'd say I agree with it 99% of the time, but there's this 1% that >>>>>>>>> makes me miss Clojure's `cond->` >>>>>>>>> <https://clojuredocs.org/clojure.core/cond-%3E>. The concrete >>>>>>>>> example that made me write this PR was this: I'm writing a small >>>>>>>>> internal >>>>>>>>> library to build requests for a third party service. There are some >>>>>>>>> options >>>>>>>>> that, if included, requires me to add headers to a request. The code >>>>>>>>> looks >>>>>>>>> something like this: >>>>>>>>> >>>>>>>>> ```elixir >>>>>>>>> def request_entity(opts) do >>>>>>>>> modified_since = Keyword.get(opts, :modified_since) >>>>>>>>> entity_id = Keyword.get(opts, :entity_id) >>>>>>>>> >>>>>>>>> Req.new(url: "example.com") >>>>>>>>> |> add_x() >>>>>>>>> |> authorize() >>>>>>>>> |> add_body() >>>>>>>>> |> then(&if(modified_since, do: Req.put_header(&1, >>>>>>>>> "If-Modified-Since", modified_since), else: &1)) >>>>>>>>> |> then(&if(entity_id, do: Req.put_header(&1, "X-Entity-Id", >>>>>>>>> entity_id), else: &1)) >>>>>>>>> |> Req.request() >>>>>>>>> end >>>>>>>>> ``` >>>>>>>>> >>>>>>>>> And I'd much rather write something like this instead: >>>>>>>>> >>>>>>>>> ```elixir >>>>>>>>> def request_entity(opts) do >>>>>>>>> modified_since = Keyword.get(opts, :modified_since) >>>>>>>>> entity_id = Keyword.get(opts, :entity_id) >>>>>>>>> >>>>>>>>> Req.new(url: "example.com") >>>>>>>>> |> add_x() >>>>>>>>> |> authorize() >>>>>>>>> |> add_body() >>>>>>>>> |> then(&Req.put_header(&1, "If-Modified-Since", >>>>>>>>> modified_since), if: fn_ -> modified_since end) >>>>>>>>> |> then(&Req.put_header(&1, "X-Entity-Id", entity_id), if: fn _ >>>>>>>>> -> entity_id end) >>>>>>>>> |> Req.request() >>>>>>>>> end >>>>>>>>> ``` >>>>>>>>> >>>>>>>>> You can see conciseness is not the point*, but readability, >>>>>>>>> robustness and convenience (a very subjective feeling, so feel free to >>>>>>>>> ignore the last one). >>>>>>>>> >>>>>>>>> Lastly, I know I could change the code in a million ways to avoid >>>>>>>>> the pattern altogether, maybe even resulting in a cleaner result, but >>>>>>>>> I >>>>>>>>> feel this small addition would be a nice to have, and is something I >>>>>>>>> miss, >>>>>>>>> even if rarely. >>>>>>>>> >>>>>>>>> * I left the conciseness out of the picture because I think it's >>>>>>>>> way less important, but it does play a bit of a part. The actual >>>>>>>>> example >>>>>>>>> ends up needing to break the `if` into more lines, which doesn't read >>>>>>>>> as >>>>>>>>> good in the middle of the piping. >>>>>>>>> Em sábado, 3 de agosto de 2024 às 19:09:10 UTC-3, gva...@gmail.com >>>>>>>>> escreveu: >>>>>>>>> >>>>>>>>>> You can already capture the `if` and do it as a one-liner >>>>>>>>>> >>>>>>>>>> x |> then(&if(pred(&1), do: f(&1), else: &1)) >>>>>>>>>> >>>>>>>>>> so you don't gain much yet add more complexity. Additionally, >>>>>>>>>> your suggestion only implies what happens if `pred` is falsy while >>>>>>>>>> mine is >>>>>>>>>> clear. >>>>>>>>>> >>>>>>>>>> -Greg Vaughn >>>>>>>>>> >>>>>>>>>> > On Aug 3, 2024, at 4:16 PM, Caio Oliveira <cai...@gmail.com> >>>>>>>>>> wrote: >>>>>>>>>> > >>>>>>>>>> > x >>>>>>>>>> > |> then(fn val -> >>>>>>>>>> > if pred(&1) do >>>>>>>>>> > f(val) >>>>>>>>>> > else >>>>>>>>>> > val >>>>>>>>>> > end) >>>>>>>>>> > >>>>>>>>>> > Into this: >>>>>>>>>> > >>>>>>>>>> > x |> then(&f/1, if: &pred/1) >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> -- >>>>>>>>> >>>>>>>> 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/3c468ed7-1db6-46e3-bf23-45c21e501b3bn%40googlegroups.com >>>>>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/3c468ed7-1db6-46e3-bf23-45c21e501b3bn%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/uM_M-DWh42A/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/CANnyohay83mzBJRgz%3Dy8RZ6cp257u4t8zSfyMKMOfqmSTMvY2A%40mail.gmail.com >>>>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/CANnyohay83mzBJRgz%3Dy8RZ6cp257u4t8zSfyMKMOfqmSTMvY2A%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/CAJLj4H9%3DsUxyupijoKeJbY1TVYQbjoGF7ALzG9_UT8LF8d655A%40mail.gmail.com >>>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/CAJLj4H9%3DsUxyupijoKeJbY1TVYQbjoGF7ALzG9_UT8LF8d655A%40mail.gmail.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/uM_M-DWh42A/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/CANnyohZzP5KK-JoXV4BdSg4oGjzgW4AfxOeDu6V7SDqBopR0Ww%40mail.gmail.com >>>>>> <https://groups.google.com/d/msgid/elixir-lang-core/CANnyohZzP5KK-JoXV4BdSg4oGjzgW4AfxOeDu6V7SDqBopR0Ww%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/CAJLj4H8tdQsK_bpxJXaYQawdLLOsYFt%2BKLL28yDqqbv1rUNP5A%40mail.gmail.com >>>>> <https://groups.google.com/d/msgid/elixir-lang-core/CAJLj4H8tdQsK_bpxJXaYQawdLLOsYFt%2BKLL28yDqqbv1rUNP5A%40mail.gmail.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/uM_M-DWh42A/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/28FA3E4F-762F-40C9-864B-053CC20F4938%40binarynoggin.com >>>>> <https://groups.google.com/d/msgid/elixir-lang-core/28FA3E4F-762F-40C9-864B-053CC20F4938%40binarynoggin.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/CAJLj4H9KN3aN__3YnuD21F%2B9wmzXGp0uE4_9CUUw_zvez2poNA%40mail.gmail.com >>>> <https://groups.google.com/d/msgid/elixir-lang-core/CAJLj4H9KN3aN__3YnuD21F%2B9wmzXGp0uE4_9CUUw_zvez2poNA%40mail.gmail.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> >>> >>> -- >>> Austin Ziegler • halosta...@gmail.com • aus...@halostatue.ca >>> http://www.halostatue.ca/ • http://twitter.com/halostatue >>> >>> -- >>> 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/uM_M-DWh42A/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/CAJ4ekQuEvC%3D0ptqFOk8TDntk0YLK6pT5wn2mo%3DNRoYd50D6aCQ%40mail.gmail.com >>> <https://groups.google.com/d/msgid/elixir-lang-core/CAJ4ekQuEvC%3D0ptqFOk8TDntk0YLK6pT5wn2mo%3DNRoYd50D6aCQ%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/CAJLj4H_Yt6-%2B_TkfeNR-q28COp8b4dicopT88CYujWXX4XRM5Q%40mail.gmail.com >> <https://groups.google.com/d/msgid/elixir-lang-core/CAJLj4H_Yt6-%2B_TkfeNR-q28COp8b4dicopT88CYujWXX4XRM5Q%40mail.gmail.com?utm_medium=email&utm_source=footer> >> . >> > > > -- > Austin Ziegler • halosta...@gmail.com • aus...@halostatue.ca > http://www.halostatue.ca/ • http://twitter.com/halostatue > > -- > 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/uM_M-DWh42A/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/CAJ4ekQuNF-%3Dtsf748N9B81QegR5qMb8p%2BSSHC%3DWtRe0gb5Tthg%40mail.gmail.com > <https://groups.google.com/d/msgid/elixir-lang-core/CAJ4ekQuNF-%3Dtsf748N9B81QegR5qMb8p%2BSSHC%3DWtRe0gb5Tthg%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/CAJLj4H9KKCWNj%2BZdUPjTi2MBYUH7iou-qAHksyKteESYBO2FDQ%40mail.gmail.com.