--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/6B6AB775-F3D5-40E5-BFBD-9852FBCBD1D0%40gmail.com.
%{"items" => nil} |> get_in(["items", Access.at(0, nilsafe: true)
Thanks, José. I agree with the need to be consistent. I will look at the bigger picture, though, like Manfred I find the addition of "maybe" to be awkward, so my preference is to have the existing recommended functions in the Access module intended for use with get_in to be consistently nil safe. I'm open to more ideas, too.
-Greg Vaughn
> On Jan 28, 2020, at 12:45 PM, José Valim <jose...@dashbit.co> wrote:
>
> The proposal is reasonable however it would introduce an inconsistency since the other selectors in Access, such as Access.key, are also not nil safe. So whatever solution we choose needs to be consistent.
>
> One possible suggestion is to introduce a "Access.maybe" that composes but composition would have to be back to front:
>
> %{"items" => nil} |> get_in(["items", Access.at(0) |> Access.maybe])
>
> Another idea is to introduce maybe_at, maybe_key, maybe_key! and so on. But I am not sure if this is desirable. Thoughts?
>
> On Tue, Jan 28, 2020 at 7:33 PM Greg Vaughn <gva...@gmail.com> wrote:
> I propose that the function returned from Access.at/1 special case nil such that the overall Kernel.get_in/2 call returns nil instead of raising an error.
>
> Rationale:
> I originally blamed this on Kernel.get_in/2 and I'd like to thank Eric Meadows-Jönsson for explaining the underlying reason to me on Slack.
>
> I like to think of Kernel.get_in/2 as a nil-safe way of plucking values out of nested data structures, but I learned today that is only partially correct. The nil-safety comes from the underlying Access.get/2 calls. The docs for get_in includes:
>
> In case any of the entries in the middle returns nil, nil will be returned as per the Access module:
> iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
> iex> get_in(users, ["unknown", :age])
> nil
>
> and I expected use of Access.at/1 in my keys to act similarly, but it doesn't. For example:
>
> iex(185)> %{"items" => ["desired_value"]} |> get_in(["items", Access.at(0)])
> "desired_value"
> iex(186)> %{"items" => nil} |> get_in(["items", Access.at(0)])
> ** (RuntimeError) Access.at/1 expected a list, got: nil
> (elixir) lib/access.ex:663: Access.at/4
>
> I propose that the function returned from Access.at/1 special case nil such that the overall get_in/2 call returns nil instead of raising an error. I have not dug into the source yet but I'm happy to work up a PR if there is interest in this change.
>
> -Greg Vaughn
>
> --
> You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to elixir-l...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/6B6AB775-F3D5-40E5-BFBD-9852FBCBD1D0%40gmail.com.
>
> --
> You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to elixir-l...@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/1ae0b9d3-9471-4750-8734-281033e9a1dc%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/22988265-AB94-4666-894B-9ECF7B87905D%40gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/4CE0D4F8-A341-4832-AC94-BDBC0D7E0911%40gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/E9C59B12-1663-45E9-A8C3-163F75895D52%40gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/D19053D7-E80B-4844-856E-2B63E6C71AE5%40gmail.com.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/B090C168-E959-484B-ADB1-A81AB177D732%40gmail.com.
The underlying problem to me seems that in this example `nil` is used both as a default to be returned when nothing is found and as an actual value in one of the data structures.
I do not think that finding a way to treating them as 'the same' is a good solution to the problem, because:
- It is difficult to do this in a backwards-compatible way.
- As José highlighted, there is a difference between `get_in` and
the other Access-based `*_in`-calls.
- It becomes more difficult to reason about the code because the
difference between the two approaches is very subtle.
Instead, I think that rather than treating this symptom
(frustration at seemingly inconsistent behavior),
we should tackle the underlying cause (the behavior being
consistent but confusing):
If we'd have an alternative to `get_in` that does not rely on
`nil` being used as default 'nothing found' value, then the
difference between the examples becomes immediately apparent.
We e.g. could introduce something named e.g. `fetch_in` that makes a clear distinction between values that are not in the nested collection vs values (like 'nil') that are there:
```
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}
iex> fetch_in(users, ["unknown", :age])
:error
iex> %{"items" => ["desired_value"]} |>
fetch_in(["items", Access.at(0)])
{:ok, "desired_value"}
# This is the important difference
# It is clear here that `nil` is there rather than the
default being returned.
iex> %{"items" => nil} |> fetch_in(["items"])
{:ok, nil}
# Therefore, it now makes sense to the programmer
# that an error is raised here
iex> %{"items" => nil} |> fetch_in(["items",
Access.at(0)])
** (RuntimeError) Access.at/1 expected a list, got: nil
(elixir) lib/access.ex:663: Access.at/4
`fetch_in` is the most descriptive name that I thought of just
now because of its return type being similar to `Access.fetch`
(just like the other `*_in` functions).
However, since `Access.fetch` depends on struct-modules
overloading the behaviour, we might want to search for yet another
name, because it seems much more useful if we could use it for any
structs.
Another problem with introducing `fetch_in` is that it adds a new function to Kernel.
Another approach (which would also tackle the aforementioned
issue of wanting to use the new function on structs that do not
overload `Access.fetch`) would be to introduce an option as third
parameter (or keyword parameter?)
for `get_in` that would switch to explicit, ok/error-tuple-based,
retrieval.
As for actually solving Greg's practical problem at hand: If you
want to treat explicit `nil`'s the same way as 'the key does not
exist',
then what about removing any fields that point to a `nil` value
before performing your '`get_in` and friends'-based validation?
~Marten/Qqwy
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4JH%3DjjhdWOkj8xErUvOPqwn4FYUvCXJcaRe4og4pUQwRA%40mail.gmail.com.
As a quick addendum: Scratch the "because it seems much more
useful if we could use it for any structs."-part of my previous
message.
I mistakenly remembered that `get_in` allows you to fetch keys
from structs that do not themselves follow the `Access` behaviour,
which is nonsense :-).
~Marten/Qqwy
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/44a7d143-21d0-6450-3e30-d6001a6867c8%40resilia.nl.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/c0b39030-ac84-846f-d3c0-9f9a85401fd2%40resilia.nl.
A bit verbose but it will give you control exactly what to do and when, without coupling to the current functions.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/4389ddde-4aff-4002-b5a6-bd519d693065n%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/4389ddde-4aff-4002-b5a6-bd519d693065n%40googlegroups.com.