Help composing/abstracting list of keys to traverse nested data structures using the Access module

update_in/3 and friends are plain functions, and their argument is a plain list (of anonymous functions). The macro-sorcery might be necessary to do things with update_in/2, but not here.

You could do this with functions:

defp toys_for(pet_id) do
  [
    Access.key!(:pets),
    Access.filter(&(&1.id == pet_id)),
    Access.key!(:toys)
  ]
end

# in the created/deleted callbacks:
update_in(
  person,
  toys_for(pet_id),
  fn toys -> [toy | toys] end
) 

# in the updated callback:
update_in(
  person,
  toys_for(pet_id) ++ [Access.filter(&(&1.id == toy_id))],
  fn toy -> %{toy | new_attributes} end
) 

You could also split this differently - combine Access.key!(:pets) with the Access.filter and name it pet_with_id or similar.