small medium large xlarge

Generic-user-small
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
       next_fn.(row)
     end
   end
end

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 et.al) 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?

Dave_gnome_head_isolated_pragsmall
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.

Dave

Generic-user-small
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!

Dave_gnome_head_isolated_pragsmall
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

Generic-user-small
18 Jan 2016, 21:44
Nathan Hornby (5 posts)

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

Generic-user-small
29 Mar 2017, 02:11
David Rodriguez Ayala (1 post)

Source code is here: https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/kernel.ex#L1821

: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