Hi! Thanks for the elaborate response!
> You have to write "write_y()" function anyway, so create a version that has a no-op version that simply returns the argument: Sometime write_y is already there and then you need to write a wrapper function. Also I really try to avoid putting a boolean inside every function or letting it check for nil input values, i try to make this decision explicit at the caller location. > You can write an anonymous function inline that contains your logic pretty easily, although I don't think this syntax is well-liked, it's at least clear. Yes, now that then/2 is here it's even easier, but I agree that still the readability is not ideal :) > What you don't say whether or not the function called in the "then" function actually returns the original piped value, or if it returns the result of the "write_y" function like then/2 it should return the result of the write_y function > The idea of "conditionally apply sales tax to a price calculation" comes to mind, but you would encapsulate the logic into the function anyway, because the actual sales tax rate is different per state. I'd expect that to be more like: > item > |> apply_quantity_discount(quantity) > |> apply_sales_tax(state). # apply_sales_tax would know whether or not to do anything based on the state This example actually makes sense because the decision whether or not to apply function 'apply_sales_tax' is solely based on the 'state' argument. But what about 'apply_quantity_discount' ? Maybe we should only apply that function in certain situations and should the amount be computed inside that function? Of course you can put all this logic inside *apply_quantity_discount* but depending on how complex the *decision* and the actual *transformation* is, you might want to split it up into two separate functions. And then you have to write a maybe_ variant or put a boolean check inside the transformation function. Overall I'm also not 100% convinced that this is needed, but somehow it still feels like that from a functional programming perspective the concern of "deciding" and actually "applying" a transformation should be separate from each other, especially when the "deciding" part is not based on the input arguments of the transformation. > I really can't think of too many, and wonder if maybe there's just a better way for you to think about the problem in the first place? I'll dig more into our projects and see if I can come up with a list of real-world examples. Anyways, there's plenty food for thought here, thanks :)! Op vrijdag 8 oktober 2021 om 17:35:47 UTC+2 schreef ...Paul: > I think this has been brought up a few times and not sure there's a great > need for it. It can't (shouldn't) happen _that_ many times, because if > you're not going to do something with the result of a function the chances > are you should be bothering to do_something() to the function anyway. And > even then there are a few other ways this can be done that are arguably > cleaner. > > 1. You have to write "write_y()" function anyway, so create a version > that has a no-op version that simply returns the argument: > > def write_y(y, arg0, arg1, skip \\ false) > def write_y(y, _, _, false), do: y > def write_y(y, arg0, arg1, _) do > .... > > 2. You can write an anonymous function inline that contains your logic > pretty easily, although I don't think this syntax is well-liked, it's at > least clear. > > some_value > |> do_something() > |> (fn y -> > if y_needed, do: write_y(y, arg0, arg1), else: y > end).() > > 3. What you don't say whether or not the function called in the "then" > function actually returns the original piped value, or if it returns the > result of the "write_y" function. There _has_ been discussion about a > "tap" function that would let you basically encapsulate the logic in the > previous inline function example but be _sure_ to always return the > original piped value (so you don't forget the "else: y" bit). I don't > remember if that was something that eventually was decided or not, but it's > a simple function to write yourself if you really want it: > > def tap(value, block) do > block.(value) > value > end > > some_value > |> do_something() > |> tap(fn y -> > if y_needed, do: write_y(y, arg0, arg1) > end) > > The difference between #2 and #3 is what is ultimately returned if > "y_needed". The "tap" function sees a lot more use cases, I think, because > "side effects" are a more frequent occurrence -- tossing something into a > job queue, writing off a log message that isn't just "IO.inspect", > triggering an asynchronous operation, etc. Things where you want to do > something with an intermediate value but don't want to change it in the > process. > > I'm hard pressed to come up with a really great example of where you may > want to conditionally do something to a value inline that isn't better > handled with functional logic. The idea of "conditionally apply sales tax > to a price calculation" comes to mind, but you would encapsulate the logic > into the function anyway, because the actual sales tax rate is different > per state. I'd expect that to be more like: > > item > |> apply_quantity_discount(quantity) > |> apply_sales_tax(state). # apply_sales_tax would know whether or not to > do anything based on the state > > You say you have to do this "often" -- can you give us some real-world > examples of your code logic that you feel necessitates this kind of thing? > I really can't think of too many, and wonder if maybe there's just a better > way for you to think about the problem in the first place? > > ...Paul > > > > On Fri, Oct 8, 2021 at 3:59 AM 'Damir' via elixir-lang-core < > [email protected]> wrote: > >> Often I have a function that I want to apply to a pipeline conditionally. >> I'm forced not to either write a "maybe_do_x" function or use an inline >> check with then/2. >> >> Now that we have then/2, I'm thinking it might be nice to have then/3 so >> that we don't need "maybe_" functions anymore, improving composability. >> Example usage: >> >> y_needed = # true or false >> >> some_value >> |> do_something() >> |> then(y_needed, &write_y(&1, arg0, arg1) ) >> >> -- >> > 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/9b4501b4-e114-4421-b61a-74240d160a15n%40googlegroups.com >> >> <https://groups.google.com/d/msgid/elixir-lang-core/9b4501b4-e114-4421-b61a-74240d160a15n%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/71ebb1d0-c403-4054-aa79-60dd8d6a3a96n%40googlegroups.com.
