rm-rf-etc
Nicer way to pipe?
This is basically the same question I’ve asked before, I’m just wondering if anything has changed since I last asked, or if there’s any other ways to do this thing that I am doing.
I know there’s been proposals for alternate ways to pipe some input into an argument slot other than the first position, and I understand why those got shot down, so I’m not really asking about that.
Here’s an example of a function I wrote recently, and shows a style I’ve been doing kind of frequently.
def get_meter(num, total) do
Decimal.div(num, total)
|> Decimal.mult(10)
|> Decimal.round(0)
|> Decimal.to_integer()
|> (fn level ->
Enum.map(0..10, fn ^level -> "|"; _ -> "-" end)
|> Enum.join()
end).()
end
And this is another way to do the same thing, which I kind of like because we don’t have to add the extra parentheses, but we do have to add an extra level of indentation, which I find annoying.
def get_meter(num, total) do
Decimal.div(num, total)
|> Decimal.mult(10)
|> Decimal.round(0)
|> Decimal.to_integer()
|> case do
level ->
Enum.map(0..10, fn ^level -> "|"; _ -> "-" end)
|> Enum.join()
end
end
Using case is really nice when we truly have different conditions to handle in different ways, and I love that we can then pipe the result from the case statement/operator/function/macro/whatever-the-correct-term-is into whatever else comes after, but in the example shown, I don’t like it much.
The best alternative I can find to make this a little cleaner would be this:
def do_func(arg, func), do: func.(arg)
def get_meter(num, total) do
Decimal.div(num, total)
|> Decimal.mult(10)
|> Decimal.round(0)
|> Decimal.to_integer()
|> do_func(fn level ->
Enum.map(0..10, fn ^level -> "|"; _ -> "-" end)
|> Enum.join()
end)
end
Would be nice if Elixir had a built-in Kernel.do_func/2.
Most Liked
benwilson512
Exactly. It is not obvious what the intent of (&Enum.map(0..10, fn ^&1 -> "|"; _ -> "-" end)).() is at all. However if you name it, then it’s a lot clearer.
RudManusachi
Another approach could be to use capture operator & to create anonymous function. Plus after it we could take Enum.join/1 out to the “main pipeline”
def get_meter(num, total) do
Decimal.div(num, total)
|> Decimal.mult(10)
|> Decimal.round(0)
|> Decimal.to_integer()
|> (&Enum.map(0..10, fn ^&1 -> "|"; _ -> "-" end)).()
|> Enum.join()
end
However, I think overall this pattern of wrapping code into anonymous function just to fit into pipeline is considered as bad a bad practice and it’s more preferred to break it into separate functions or intermediate values
(see https://github.com/lexmag/elixir-style-guide#anonymous-pipeline
rm-rf-etc
'Aight, good feedback. You all were right, I moved some things around and the code got better. Thanks everyone.
rm-rf-etc
Oh hey! This is not bad! Just realized I could make this small change.
def do_func(arg, func), do: func.(arg)
def get_meter(num, total) do
Decimal.div(num, total)
|> Decimal.mult(10)
|> Decimal.round(0)
|> Decimal.to_integer()
|> do_func(&Enum.map(0..10, fn ^&1 -> "|"; _ -> "-" end))
|> Enum.join()
end







