PipeAssign - Keep the Pipe, Capture the Value

Hey folks,
I made a tiny macro that lets you assign a piped value to a variable without breaking the flow. For example:

data
|> transform()
|> process()
|> finalize()
|> assign_to(result) # Assign to result variable

or even during the pipeline:

data
|> transform()
|> assign_to(step1)  # Capture without breaking flow
|> process()
|> assign_to(step2)
|> finalize()
|> assign_to(result) # Assign to result variable

Sure, it’s not idiomatic — but we’ve got then/1, tap/1, even throw/1, so I got curious about what assign from pipe could look like.

Had to jump through a few hoops to avoid compiler warnings, but it works smoothly now.

Docs: pipe_assign v1.0.0 — Documentation
Hex.pm: pipe_assign | Hex
Repo:

9 Likes

Bookmarked for the very next time I need it

Thanks!

1 Like

You could achieve the same result and readability by using the with clause in my opinion:

with step1 <- transform(data),
     step2 <- process(step1),
     result <- finalize(step2) do
  # do something with variables
end
5 Likes

Yes. But I love pipes :heart:

4 Likes

Pipe-ists gonna pipe :person_shrugging:

5 Likes

I look forward to trying this out.

How would you compare and contrast this library’s aims and functionality with the ok library?

ok is a syntactically shorter with and then alternative. It’s not suitable to assign to variable in pipe flow.

pipe_assign is an alternative for an equal sign but not before pipe flow, but after. It’s suitable to assign to variable in pipe flow.

1 Like

I wonder what compile warnings you were referring to. The last time I checked, this works:

  defmacro bind_to(value, name) do
    quote do
      unquote(name) = unquote(value)
    end
  end
2 Likes
result = 0

[1, 2, 3]
|> Enum.map(&(&1 * 2))
|> Enum.sum()
|> bind_to(result)

warning: variable “result” is unused

If you are not going to use the old result why initialize it? If you are not going to use the new result why bind to it? IMHO this warning is useful.

4 Likes

Now when I think about it… you’re right! =)
I’ll change the behavior.
Thank you.

2 Likes

LOL just got a “popular link” badge for Put The Pipe Down :sweat_smile:

I know I’m an old fuddy duddy but if I remember my after school special lessons correctly: hitting the pipe once in a while, or even often, is a recipe for a good time had by all! But don’t overdo it there, eh (I’m also Canadian… where we didn’t have after school specials so… whatever)

Like my grandma used to say: “Just write code any ol’ fool can understand so we can make some money and get back to piping.”

1 Like

Honestly, this should be part of Kernel.

Sure, it’s not idiomatic

Why? then/1 and tap/2 are there, and I can’t read anything bad about them in the docs.

1 Like

Because those are local to the pipe, they aren’t binding values.

This inverts the normal order of binding where the thing being bound is normally on the LHS and now it’s on the RHS. The bit where binding a value interrupts a pipe is a Good Thing. You want that visual cue to say “this value has been shadowed or set”

6 Likes

If you go back and read discussions leading up to the introduction of then, it was a compromise instead of a positional pipe operator—to paraphrase Calvin and Hobbes: “Nobody likes a good compromise.” As for tap, this lets you “put side-effects in a pipeline.” (:face_with_raised_eyebrow:) This has some uses in debugging but otherwise goes against the FP spirit of making side-effects stand out.

It should go without saying that this is all subjective, but dealing with “creative” pipelines in production code makes me crazy.

1 Like
step1 = doStep1(input)
step2 = doStep2(step1)
step3 = doStep3(step2)
output = doStep4(step3)

vs

input
|> doStep1()
|> doStep2()
|> doStep3()
|> doStep4()

First come the headache of choosing names (same with assign_to, agree).

Then come the headache if some step must be removed or reordered.
With intermediary variables, all names must be rewritten.
With pipes, it’s just a matter of commenting or moving lines.

I’m not sure I would ever use this but as someone who uses then/2 like a madman I like the look of this.

The inline capture is easily my favorite part. Is it a good idea? I don’t actually see a problem with this at all expect that it’s “weird.” If you’re expecting it and understand it though? It’s not hard to understand.

Definitely cool either way.

Are you motivating just regular pipes or are you motivating the OPs library? Cause the arguments you’re making only really apply to regular pipes and yes, that’s why they are great.

1 Like