Many significant apologies for reopening this very-very-well-treaded topic,
but I do want to advocate this *one* more time.
The pipe operator is used to illustrate the flow of data transformation,
but while functional programming is very expressive, there are times when a
signature requires the data in question to be used as a different argument.
In those cases, you must either break the pipe chain (and thus the flow of
data transformation) to assign the transformed data to a variable, or send
the data through an anonymous function to continue the program flow.
Take a process where data should be transformed before being saved to a
database:
# intermediary value
def encrypt_password(user, password) do
encrypted = password
|> Salt.add
|> BCrypt.hash
put_in(user[:hashed_password], encrypted)
|> EctoOrSomething.UpdateIGuess.ImStillNewHere
end
# anonymous function wrap
def encrypt_password(user, password) do
password
|> Salt.add
|> BCrypt.hash
|> (fn(encrypted) -> put_in(user[:hashed_password], encrypted) end).()
# or |> (&(put_in(user[:hashed_password], &1)).()
# but considerably messy either way
|> ...
end
The main issue in conceptualizing a more elegant workflow to represent
these transformations is that any construct implemented will make an
affordance in one direction or the other. A pipe-to-argument operator would
offer strong reusability, but proposals in this group suggested a token
that poorly expresses its usage (I'm thinking of a thread where ~| had been
proposed as an anonymous variable binding with |>) . The other proposals
are for inflexible cases that would necessitate more extensions for further
implementations, and/or borrow symbols from other languages that are
ambiguous in their usage (such as proposals for <|, |<, and |>> as tokens
for pipe-to-last).
I think Elixir is in a unique position to solve this problem, though. If I
could draw a correlation, the pin macro, Kernel.^/1 is used to bind a
variable in at a location for pattern-matching...
good_status = "200"
{^good_status, response} = fetch("www.example.com")
# expands to {"200", response} = fetch("www.example.com")
I believe there's syntactic justification for a new pipe macro, Kernel.|^/2,
that would interact with a Kernel.^/0 macro, to combine the concepts of the
pipe and the pin. Assuming the current pipe operator symbolically
translates | to "pipe to" and > to "the left", |^ would represent "pipe to
the pin". The pin would appear at least once in the expression on the right
in order to represent where the expression on the left will appear, expanded,
like such:
def encrypt_password(user, password) do
password
|> Salt.add
|> BCrypt.hash
|^ put_in(user[:hashed_password], ^)
|> ...
# expands to ...(put_in(user[:hashed_password],
BCrypt.hash(Salt.add(password)))
end
Benefits to this approach:
- The current pipe operator behavior remains pristine
- ^/0 continues to act as a reference to expanding a variable
- No introduction of new symbolic concepts
- Syntax is flexible enough to expand to any (or multiple) argument
position
# more contrived examples!!
> map = %{little_bunny: %{}}
> :foo
|^ put_in(map[:little_bunny][^], ^)
%{little_bunny: %{foo: :foo}}
> "Ton" |^ (^<>"y! " <> ^<>"i! " <> ^<>"é!")
"Tony! Toni! Toné!"
Again, I know there've been many discussions about the drawbacks to
implementing a new pipe mechanism, and the Elixir community may have
already come to a consensus around how to handle transformations like this,
but I think the longevity of this topic points to a desire for some kind of
better solution.
If everyone is tired of talking about this, I'll happily let it end here,
but I thought one more perspective couldn't hurt.
Thanks for reading! ^_^
On Wednesday, July 9, 2014 at 5:10:19 AM UTC-7, José Valim wrote:
>
> at the risk of being painfully obvious, this works:
>>
>> :crypto.rand_bytes(8)
>> |> Base.encode16
>> |> (&("--------FormDataBoundary" <> &1)).()
>>
>
> Yup, this form works and is usually the solution proposed for such cases.
>
> I concur that allowing such forms would be a special case for pipe and
> possibly confusing in the long term. However, given that many were
> expecting it to work, I decided to gather everyone's feedback. And based on
> this thread, it doesn't seem we should change the pipe operator.
>
>
> *José Valim*
> www.plataformatec.com.br
> Skype: jv.ptec
> Founder and Lead Developer
>
>
>
--
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/5e14b193-4102-4ca9-991d-273b572727c2%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.