Implement a function like `Map.put/3` but with flipped arguments for better piping

85 views
Skip to first unread message

Zach Daniel

unread,
Oct 4, 2017, 11:40:17 PM10/4/17
to elixir-lang-core
In our codebase, the ideal function in many cases is just a series of piped calls. There are plenty of functions where something like `with` makes more sense, but we usually prefer pipes.

One of the patterns I see all over the place is this:

  def bad_example(map) do
    value =
      map
      |> get_in([:post, :comments, Access.all(), :likes])
      |> Enum.sum()

    Map.put(:sum_of_comment_likes, value)
  end

I think implementing something like this (I haven't though of the best name, but I'm sure there is a better one) would clean up a lot of code:

defmodule MyMap do  
  def set_into(value, map, key) do
    Map.put(key, map, value)
  end
end

This would allow cleaner piping, like so:

  def nice_example(map) do
    map
    |> get_in([:post, :comments, Access.all(), :likes])
    |> Enum.sum()
    |> Map.set_into(map, :sum_of_comment_likes)
  end

What would you all think of having something like this in the standard library in `Map`?

José Valim

unread,
Oct 5, 2017, 1:58:54 AM10/5/17
to elixir-l...@googlegroups.com
Definitely not. :) defining private functions in your codebase that help in those scenarios is the way to go.
--


José Valim
Founder and 
Director of R&D

Zach Daniel

unread,
Oct 5, 2017, 10:01:40 AM10/5/17
to elixir-lang-core
I just don't want to end up with either:

* The same private function defined in many modules
* A MapHelpers module that contains some of our utilities around maps, making it difficult to determine where you're looking.
* A library that we share between our projects that ends up as our extension of the stdlib.

I don't mind about any one instance in particular, I think I'm really just curious about how the language might be informed about high demand for things to be in the stdlib.  I imagine there are a lot of small utility functions that are in wide usage, but lacking the benefit of standardization that the stlib could bring. 

Is there any common utility library that acts as a sort of pre-stdlib, like lodash in javascript?

Again, no big deal for the one I proposed here, just wondering the best resources for this kind of thing.

Zach Daniel

unread,
Oct 5, 2017, 10:07:43 AM10/5/17
to elixir-lang-core
What about a pipe operator that puts the subject as the last argument?

José Valim

unread,
Oct 5, 2017, 10:10:50 AM10/5/17
to elixir-l...@googlegroups.com
The goal of the standard library is not to provide all common cases developers need, especially one that is addressed with one line of code. Instead we want you to enable you to write this code - which is possible today. This is the third proposal to the Map module this week and it is easy to see it would quickly get bloated if it was supposed to handle all of those concerns.

* The same private function defined in many modules

I personally don't see anything wrong with such a small private function defined in a handful modules. It is part of our job to break large code apart into small functions and those small functions only exist to support the current implementation. To quote Go proverbs: "A little copying is better than a little dependency".

What about a pipe operator that puts the subject as the last argument?

This has been proposed and rejected a couple times in this mailing list and in the forum. I would recommend you to search previous topics. :)

Zach Daniel

unread,
Oct 5, 2017, 11:00:57 AM10/5/17
to elixir-lang-core
I totally agree with that proverb, and we definitely don't do the religious following of DRY that is such an antipattern all the time. I just worry that maybe there is an entire legion of utilities that are in use across the board, and maybe 2 or 3 of those are integral enough to be a part of the standard library and maybe another 10-20 of those could standardize on a much more performant version than the simple helpers that many developers might end up using in the interest of time, and maybe another 20 of those are utilities people never even thought of using but could really help them out. I know browsing the stdlib functions and documentation really helps our new elixir engineers. I definitely see your point about bloat. I *may* find/start a common utility library, just to find out whether or not I'm the only one who thinks it may be useful, and to maybe find new and better versions of utilities that might help us. We're at like 60k LoC so sharing some utilities isn't out of the question.

Chris Russo

unread,
Oct 23, 2017, 11:27:19 AM10/23/17
to elixir-lang-core
Actually, what would be really cool is a Kernel macro similar to put_in that would handle various forms of this, let's say it was called "flow_into".  That would allow all of the mechanics of put_in() to work in a pipe.

 def nice_example(map) do
    map
    |> get_in([:post, :comments, Access.all(), :likes])
    |> Enum.sum()
    |> flow_into(map[:sum_of_comment_likes])
  end

I really found the mechanics of updating complex structures to be intimidating when I first started using Elixir.  put_in was a great discovery for me that allowed me to make progress without worrying about the more mundane mechanics of sticking a result in an array that was in a map.

Ian Duggan

unread,
Dec 1, 2017, 12:05:51 AM12/1/17
to elixir-lang-core

On Wednesday, October 4, 2017 at 8:40:17 PM UTC-7, Zach Daniel wrote:
    |> Map.set_into(map, :sum_of_comment_likes)

So one particular convention that this proposal would break is that the functions in a module tend to have that type as the first argument. That makes it really easy to know which module to reach for next in a pipeline.

--Ian 
Reply all
Reply to author
Forward
0 new messages