small medium large xlarge

18 Jan 2016, 19:29
Nathan Hornby (5 posts)

I’m struggling to parse this function intended to be passed into the map accessor function get_in/2:

authors = [
  %{ name: "José",  language: "Elixir" },
  %{ name: "Matz",  language: "Ruby" },
  %{ name: "Larry", language: "Perl" }

languages_with_an_r = fn (:get, collection, next_fn) ->
   for row <- collection do
     if String.contains?(row.language, "r") do

IO.inspect get_in(authors, [languages_with_an_r, :name]) 
#=> [ "José", nil, "Larry" ] 

Namely the arguments in the anonymous function. I’m assuming that these macro functions (get_in/2 expect specific arguments in a function passed to them? It seems to be glossed over (unless I’m forgetting something from earlier in the book).

Firstly and most simply what does :get do in this context?

Secondly is anyone able to explain the whole next_fn thing to me? I’m new to the whole functional programming thing (and of course Elixir) so go easy, but this feels like somethings not fully explained?

18 Jan 2016, 19:50
Dave Thomas (389 posts)

Have a look at the documentation for get_in.

It will call a function you pass it with three parameters, The first is always :get, and the last is a function you should call to process the data you extracted.


18 Jan 2016, 21:20
Nathan Hornby (5 posts)

Thanks for the link Dave! That :get seems pretty arbitrary, doesn’t it?

I think I’m still not quite getting the role of next_fn. What does ‘invoked next’ mean in this context; invoked after what? Why’s it being called within the for loop? How’s that nil getting into that list (apposed to it only containing the values that match the if condition)?

I can see that the values that go into it are the one’s that end up in the list, but that’s all I got. Although I’d expect some kind of next_fn.(nil) in an else in that case…

Sorry if what I’m asking seems basic, parts of the puzzle feel missing for me!

18 Jan 2016, 21:30
Dave Thomas (389 posts)

My suggestion: skip this and keep reading.

Some of this stuff is not obvious. Rather than stress now, move on to other cool stuff, but remember that there’s a get_in function for poking around in nested data structures.

Then, six months from now, when you suddenly have a need to do just this, revisit the page. I’m betting it will make more sense, and having a goal will make it easier to apply

18 Jan 2016, 21:44
Nathan Hornby (5 posts)

Haha OK, will do! Thanks for the help and the great book, Dave.

29 Mar 2017, 02:11
David Rodriguez Ayala (1 post)

Source code is here:

:get is arbitrary, yes. But it is required to do the match.

def get_in(data, [h]) when is_function(h),
    do: h.(:get, data, &(&1))
def get_in(data, [h | t]) when is_function(h),
    do: h.(:get, data, &get_in(&1, t))


def get_in(data, [h]),
    do: Access.get(data, h)

The non base case (second function) is a little tricky:
get in my map, if head of list is a function ( in this case ‘languages_with_an_r’)
it sends to ‘languages_with_an_r’ function —>
:get keyword (just to match)
data (the map)
a anon function that looks like this: fn (some_data) -> get_in(some_data, t)
-–> in this particular case, you are sending a single row, and a list that says [:name]

Now, you hit the base case, because there is a single element [h], which is :name :
get in my map, if it is a function (ups!! not in this case, it is an atom)… ok next match
you access the Access.get(data, h) ( in this case Access.get(data, :name) ), say “Jose”

Now, up in the stack, the non base function expands this: &get_in(&1, [:name]) into a name: “Jose”.
So this: h.(:get, data, &get_in(&1, t)), turns out to be a “jose”, apply it to all the collection and you will have a list of names, one of them being nil because the language had no ‘r’ so it never executed any function.

You must be logged in to comment