+1. I do have to say, though, I'll be far more likely to implement the
refactor based on Rich's implementation.
One might be able to refactor it even farther, simply to "easy is hard",
but then it may lose some nuance and portability.
I really like the constructs Elixir uses to encourage code simplicity.
Pattern matching and guard signatures allow functions to take on a level of
granularity where you legitimately can write functions that perform
*exactly* one action, *exactly* the same way with the same inputs. What
emerges is a coding style where, as a general rule, you can say that if
your Elixir function needs to set a variable, it can be refactored to a
simpler function body.
My rationale for suggesting the pinpipe operator was because, within some
function bodies, the only reason you might consider introducing a variable
is so that your transformed data can be used in a specific position of your
function result. The code example of def boundary at the start of this
thread, for instance, is a self-contained, pure function as it is. The
anonymous function called at the end does not need to exist, as the SHA
sent in could just as easily be a variable:
def boundary do
sha = :crypto.rand_bytes(8)
|> Base.encode(16)
"---------FormDataBoundary" <> sha
end
DRY principles (at least, as far as The Rails Way was taught to me) would
say that if there were no other way you would want a
"---------FormDataBoundary" to be generated, then you would want to keep
this method body self-contained, and with as few introduced variables as
necessary. Pipe workflows keep that intent in focus by dissuading you from
making functions that work on anything but their inputs. When I'm in that
mindset, variables set within a function are an anti-pattern, but I would
still sooner catch myself moving the pipes to the place where they were
necessary:
def boundary do
"--------FormDataBoundary" <> (:crypto.rand_bytes(8)
|> Base.encode(16))
end # sha, you know what? uh-uh.
Coming back to being able to overload signatures as you can in Elixir,
after half a decade of not being able to in Ruby and Javascript, the above
is still far more natural to me (even though I know inside how kludgey it
feels) compared to something like:
def boundary, do: :crypto.rand_bytes(8) |> Base.encode(16) |> boundary
defp boundary(sha), do: "--------FormDataBoundary" <> sha
But almost all of my hesitation around a convention like this comes from
not being used to it. Portable, reusable functions are very good things to
have central to a language design, and if Elixir has A Right Way to
implement those, it's within the maintainers' rights to foster that happy
path and discourage what would be impure solutions according to the
language philosophy. As the community grows and more code examples become
available, fewer people will wonder about how they should do these things.
On Wednesday, May 10, 2017 at 5:39:53 AM UTC-7, Onorio Catenacci wrote:
>
> That's a great point Robert and Mr. Dijkstra certainly deserves credit.
> Thanks for reminding me about that.
>
> On Tue, May 9, 2017 at 5:44 PM, Robert Virding <[email protected]
> <javascript:>> wrote:
>
>> Actually this was said by Dijkstra long before Rich:
>>
>> “Simplicity is a great virtue but it requires hard work to achieve it and
>> education to appreciate it. And to make matters worse: complexity sells
>> better.”
>> Unfortunately all 3 points are true. He also said:
>>
>> “Simplicity is prerequisite for reliability.”
>>
>> which is also true.
>>
>> On Tuesday, 9 May 2017 14:11:46 UTC+2, Onorio Catenacci wrote:
>>>
>>> To paraphrase Rich Hickey--"Simplicity isn't easy". Figuring out those
>>> simple function signatures is not the first thing that comes to mind for
>>> most of us. But it's worth the effort.
>>>
>>>
>>> On Saturday, May 6, 2017 at 7:14:45 PM UTC-4, Josh Bourgeois wrote:
>>>>
>>>> I thought of practicing, but then I just wound up doing a lot of
>>>> thinking. Then it clicked 😬
>>>>
>>>> defmodule Words do
>>>> def count("" <> string), do: parse(string) |> Enum.reduce(%{},
>>>> &count/2)
>>>>
>>>> defp count("", map), do: map
>>>> defp count("" <> word, map) do
>>>> word |> String.replace("_", "-")
>>>> |> increment(map)
>>>> end
>>>>
>>>> defp increment(word, map), do: put_in(map[word], (map[word] || 0) + 1)
>>>> defp parse(string), do: string |> String.downcase |>
>>>> String.replace("-", "&hyph;") |> String.replace("_", "-") |>
>>>> String.replace("&hyph;", "_") |> String.split(~r/\W/u)
>>>> end
>>>>
>>>> It's just a challenge to think of meaningful names sometimes. Breaking
>>>> function/method bodies into smaller discrete blocks of work is obviously a
>>>> fundament to making code more readable, but it takes discipline and,
>>>> sometimes, a thesaurus
>>>>
>>>> Thank you for entertaining this zombie discussion once more 😶
>>>>>
>>>>> --
>> 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/dwbNOh_yNd4/unsubscribe
>> .
>> To unsubscribe from this group and all its topics, send an email to
>> [email protected] <javascript:>.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/elixir-lang-core/59c6d90d-ded2-4794-8a73-920ab9350007%40googlegroups.com
>>
>> <https://groups.google.com/d/msgid/elixir-lang-core/59c6d90d-ded2-4794-8a73-920ab9350007%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>
>
> --
> Onorio Catenacci
>
> http://onor.io
> http://www.google.com/+OnorioCatenacci
>
>
--
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/2ea77bcc-a834-4108-902d-8722213fae3f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.