# Unifying unwrapping single-value wrappers

**URL:** <https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003>\
**Category:** Internals & Design\
**Tags:** question\
**Created:** [October 16, 2023, 9:11am UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003 "2023-10-16T09:11:42Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [October 17, 2023, 12:22pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/21 "2023-10-17T12:22:54Z")

</div>

I would avoid using `nothing` as a value at all costs but I suppose if things ever get meta enough that `nothing` becomes a value, I would have no choice but to wrap them in `Some`.

> [@Raf](#):
>
> We shouldn’t mix them.
> 
> 1. should return the value of `Ref` like `= ref[]`, and not have methods for `Val`
> 2. should return `T` in `Val{T}`, and could be argued to return the type of `Ref` like `T` in `Ref{T}`
> 
> `unwrap` and `unwraptype`, or `extract` and `extracttype` could work.

Now that I’m looking at this, I think I do agree that these methods shouldn’t exist. Rather than having 1 canonical method for unwrapping an unfixed number of single-value wrappers (whether in a type parameter of a iterated type-union or in a field of 1 type), it makes more sense to have 1 canonical single-value wrapper so that everything is compatible, not just the unwrapping. `Val` exists precisely so people don’t define their own versions for compile-time calculations in APIs that require unwrapping and rewrapping to interact. Despite `Some{T}` technically fitting the bill, an immutable single-field wrapper isn’t really used like `Val` is because the value itself serves just as well for runtime calculations; `Ref` is useful because it can add mutability. (And the ship has sailed on [Some{T} being used as a scalar-izer for broadcasting](https://github.com/JuliaLang/julia/pull/35778), though a 1-element tuple seems just as good and less to type anyway). When a specific context really requires a specific wrapper type or union, it should have its specific unwrapping method that does _not_ interact with the more widespread `Val` or `Ref`.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [October 17, 2023, 12:49pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/22 "2023-10-17T12:49:21Z")

</div>

What would alternatively be useful is to create not a single function to “unwrap anything”, but to create a function for each of the common wrappers listed in this thread. The point is to create these functions with “standardized” names, either:

- `unref(Ref(x)) == x`, `unsome(Some(x)) == x`, `unval(Val(x)) == x` and so on;
- or `un(Ref, Ref(x)) == x` if going for a single function that remains explicit;
- or `inverse(Ref)(Ref(x)) == x` if basing on the existing interface in `InverseFunctions`. This can actually be added to that package right now, I think.

Currently there’s a mix that each wrapper is unwrapped very differently, and hard to guess unless you know the specific type interface. And `Val` doesn’t have an unwrapper at all.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [October 17, 2023, 1:08pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/23 "2023-10-17T13:08:11Z")

</div>

We already have `Ref(x)[]` and `something(Some(x))`, introducing another way to do the same thing would only confuse people, it’s not worth the aesthetic symmetry. Not sure how `unval` would be useful in practice because type parameters of all types are already unwrapped by where clauses of a method. I suppose it wouldn’t be too different from `ndims` or `eltype` for `AbstractArray`s, which are useful interactively.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [October 17, 2023, 1:15pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/24 "2023-10-17T13:15:29Z")

</div>

> [@schlichtanders](#):
>
> the meaning is enough well defined so that this api is useful for people (extracting a single canonical value from a wrapper).

The comparison with AbstractArray’s `parent` is apt here. That’s another function whose meaning feels like it’s clear but is impossible to use generically. And by generically, I mean doing something — _anything_ — meaningful with the result of `parent(x)` without knowing what type `x` was.

Yes, perhaps it’s an ergonomic convenience, but it’s also quite the footgun.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [October 17, 2023, 1:19pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/25 "2023-10-17T13:19:40Z")

</div>

> [@Benny](#):
>
> Aside, what is `Some` for? I read the docs and still don’t know why you would need to distinguish a “presence of `nothing`” via `Some(nothing)` from an “absence of a value” via `nothing`, I’ve only ever needed the latter.

As a concrete example, in [SVD2Julia.jl](https://github.com/Seelengrab/SVD2Julia.jl) I define a `const Option{T} = Union{Some{T}, Nothing}` type because I need to be able to express optionality in the [structs](https://github.com/Seelengrab/SVD2Julia.jl/blob/9fa2714700c10809edad79cfadb755f86a76e2cc/src/structs.jl#L171-L189) of the package. I could of course duplicate that information and have `isdefined` or boolean flags everywhere, but that robs me of type safety in case the field truly is not set; and I can’t change that fact, I need to be able to say “there is nothing here” (during parsing, e.g. [here](https://github.com/Seelengrab/SVD2Julia.jl/blob/9fa2714700c10809edad79cfadb755f86a76e2cc/src/parsing.jl#L524-L530)), because the underlying specification the package is implementing has this kind of optionality built in. Solutions other than some kind of `Option{T}` make the code much larger, much more difficult to maintain and much less resilient against bad input (in fact, using that `Option{T}` has already helped prevent bugs in the package!).

> [@mbauman](#):
>
> The comparison with AbstractArray’s `parent` is apt here. That’s another function whose meaning feels like it’s clear but is impossible to use generically. And by generically, I mean doing something — _anything_ — meaningful with the result of `parent(x)` without knowing what type `x` was.

Yes, that is a good comparison. To make this maybe a bit more concrete, unwrapping a `Ref` and then “rewrapping” it into a new `Ref` won’t work for the cases where `Ref` can/should be used (not to mention that you have to know the wrapper was a `Ref` in the first place!). By unwrapping & rewrapping you’re losing the semantic “this came from somewhere else, and must continue to refer to that other place” that is inherent to `Ref`, which is not shared by things like `Some` or `Val`.

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [October 17, 2023, 2:00pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/26 "2023-10-17T14:00:24Z")

</div>

…why would you want to do that?

I think your requirement forces the following implementation, and it seems quite useless to me?

```julia
struct NotASingleItemWrapper end
struct NullPtrError end
struct Rewrapped{T}
item::T
end
struct Primitive{T}
item::T
end
const Rewrap{T} = Union{NotASingleItemWrapper, NullPtrError, Rewrapped{T}, Primitive{T}}
@generated function universal_rewrap(x::T) where T 
       if isprimitivetype(T) return :(Primitive(x))
       elseif length(fieldnames(T)) != 1 return :(NotASingleItemWrapper())
       else return :( (isdefined(x, 1) ? Rewrapped(getfield(x, 1)) : NullPtrError() ))
       end
       end

```

(edit: fixed language, sorry for being obnoxious the first round)

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [October 17, 2023, 2:06pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/27 "2023-10-17T14:06:22Z")

</div>

> [@schlichtanders](#):
>
> Concretely I cannot imagine a single Julia user who does not understand what `extract(Some(1))` or `extract(Ref(1))` or `extract(Val(1))` would be doing if they see its documentation is saying `extract a single canonical value from a wrapper`.

Code is more likely to look like `extract(x)`. That’s a big loss in readability compared to `something(x)`. With the latter I know what kind of thing `x` is and what’s going on.

`Ref`, `Some` and `Val` are very different features, that are used in completely different mechanisms. They all “wrap” in some sense, but it seems like a superficial commonality to me. I think it would be a bad idea to base an abstraction on that, especially in Base.

I’m also unsure it’s a good idea from a “pedagogic” perspective, to make users think that unwrapping these values is the same kind of operation. If discoverability is an issue we should rather improve the documentation of `Ref`, etc.

Maybe I’m missing the big picture, would you have a concrete example of where this would be useful?

---

<div class="post-metadata">

**Author:** ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)\
**Post date:** [October 17, 2023, 2:16pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/28 "2023-10-17T14:16:29Z")

</div>

please refrain from using such aggressive and disrespectful language

---

<div class="post-metadata">

**Author:** ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)\
**Post date:** [October 17, 2023, 2:31pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/29 "2023-10-17T14:31:09Z")

</div>

To me getting the value from a single value wrapper is mentally always the same wish - but depending on which type I am currently working with (for whatever convenience the type brings), I need to search for the individual function or attribute this very type has for unwrapping.

> [@sijo](#):
>
> They all “wrap” in some sense, but it seems like a superficial commonality to me.

It is not superficial, it appears often enough that I am opening this discourse issue.

> [@sijo](#):
>
> I’m also unsure it’s a good idea from a “pedagogic” perspective, to make users think that unwrapping these values is the same kind of operation.

Maybe julia is not the right language for people who think this way. Multiple Dispatch means that calling the same function/api usually corresponds to different operations being run depending on the input type.

> [@mbauman](#):
>
> Yes, perhaps it’s an ergonomic convenience, but it’s also quite the footgun.

I don’t see the footgun. What can be bad? You have a general api to get the single value from the single-value wrapper which people get to know and simplifies their life because they don’t need to search and again for the method used for Ref vs Val, vs WeakRef, vs Some, vs custom types (e.g. SingletonIterator might be helpful).

> [@Raf](#):
>
> Personally I just define `_unwrap` manually in every single package 🙃

This seems to be the best way so far - just let everyone themselves define such a function locally. Unsatisfying, but probably the best workaround so far.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [October 17, 2023, 2:33pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/30 "2023-10-17T14:33:48Z")

</div>

> [@schlichtanders](#):
>
> Maybe julia is not the right language for people who think this way. Multiple Dispatch means that calling the same function/api usually corresponds to different operations being run depending on the input type.

Hard disagree. Multiple dispatch means that you can easily extend existing functions with new types and also easily extend existing types with new function(ality). It does not mean that we’re free to invent new semantics for what a function means.

---

<div class="post-metadata">

**Author:** ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)\
**Post date:** [October 17, 2023, 2:38pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/31 "2023-10-17T14:38:23Z")

</div>

I agree you should have some common understanding for an API, that helps the community. It is not a hard requirement in Julia, but it is very helpful, I agree.

This common understanding is also given here (“extracting a single-value from a single-value wrapper”, or the slight generalization of “extracting a canonical value from a wrapper”).

It is just natural that the same “semantics” (== common understanding) is realized in different ways for different types. (I guess the best Base example is `map`)

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [October 17, 2023, 2:49pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/32 "2023-10-17T14:49:18Z")

</div>

The point is that you can only meaningfully unwrap something if you know what that wrapper is doing. And if you already know what the type of the wrapper is, then you can use the wrapper-specific methods/accessors/etc for it.

This is different from `map` because you _can_ meaningfully call map without knowing the function or collection type — and there’s an understanding that you should (roughly) be able to do the same sorts of things with the output as you could with the input. It’s not perfect, but there’s a clear desiderata against which bugs may be filed.

That’s not to say there’s not an ergonomic advantage to sharing the same vocabulary, but that’s slightly different than what a “generic function” should ideally be.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [October 17, 2023, 2:58pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/33 "2023-10-17T14:58:00Z")

</div>

> [@schlichtanders](#):
>
> You have a general api to get the single value from the single-value wrapper which people get to know and simplifies their life because they don’t need to search and again for the method used for Ref vs Val

I would be immediately confused because extracting a value from a field of `Ref` is _not_ the same thing as extracting a value from a type parameter of `Val` to me.

Even if I do accept the broader umbrella purpose as 1 thing by 1 generic function, let’s call it `extract` here, it can’t be considered in isolation. It’s not any more valid than a generic function that, say, retrieves the 1st parameter of a parametric type’s instance; let’s call it `firstparam`. Now `extract(Val(1))` does the same thing as `firstparam(Val(1))`, and `firstparam([1])` does the same thing as `eltype([1])`. It’s very easy to create a bunch of overlapping features until there’s no clear 1 way to do each thing, so features should not exist just because they are convenient in isolation, they should also be needed. I don’t think we need 1 thing to unwrap `Ref`, `Val`, and all the other context-specific wrappers because they aren’t intended to intermingle e.g. I can’t substitute `Ref` into `foo(::Val{T}) where T = T ...` nor should I substitute `Val` into `bar(t::Ref{T}) where T = t[] ...`.

---

<div class="post-metadata">

**Author:** ![contradict](https://avatars.discourse-cdn.com/v4/letter/c/ac91a4/32.png) [@contradict](https://discourse.julialang.org/u/contradict)\
**Post date:** [October 17, 2023, 3:06pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/34 "2023-10-17T15:06:25Z")

</div>

This may be naive, but what about [`only`](https://docs.julialang.org/en/v1/base/iterators/#Base.Iterators.only)? It works for `Ref` as well as any container and seems to have the idea the rest of these want.

---

<div class="post-metadata">

**Author:** ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)\
**Post date:** [October 17, 2023, 3:11pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/35 "2023-10-17T15:11:25Z")

</div>

> [@Benny](#):
>
> features should not exist just because they are convenient in isolation, they should also be needed.

My aim with this discussion is not to add a function to julia which is needed (whatever this should mean - probably a bugfix or so is a good example). My aim is indeed to simplify the api/interface on these types so that Julia gets indeed more convenient.

It is very important to assess whether something is really convenient for most, or only for some and too confusing for others so that in total it is not a good idea to add it.

But if it is convenient for most, then I would indeed like to add it to Julia.

---

<div class="post-metadata">

**Author:** ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)\
**Post date:** [October 17, 2023, 3:12pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/36 "2023-10-17T15:12:56Z")

</div>

thanks for pushing `only` - I was not aware that this is an unwrap for `Ref`

sounds even more intuitive than `get` to me

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [October 17, 2023, 3:16pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/37 "2023-10-17T15:16:04Z")

</div>

> [@mbauman](#):
>
> if you already know what the type of the wrapper is, then you can use the wrapper-specific methods/accessors/etc for it

You may know what the wrapper is, but it’s still a bit of a mental overhead to remember all those different unwrapping functions. That’s why something uniform but still explicit can be reasonable – like `un(Ref, x)`.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [October 17, 2023, 3:34pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/38 "2023-10-17T15:34:40Z")

</div>

I dunno, but I still have it out for `parent` after it led me down [some very deep and dark rabbit holes](https://github.com/JuliaLang/julia/pull/25890#issuecomment-367146878). The existence of these functions can make it seem like you’re writing generic code (when it’s really not doing what you meant) or can entice you into futile attempts at it.

IMV the place to standardize the `parent` language is in the field name. Then you know you’re doing something very particular to the type through field access, but still have a common vocabulary. Perhaps the same is true here, but with `.value`?

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [October 17, 2023, 3:40pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/39 "2023-10-17T15:40:32Z")

</div>

For `parent` and arrays specifically, field access is often inconvenient – array types can and do define their custom properties, so that `getfield(x, :parent)` would be needed to actually access the parent field. For wrappers it can be less of an issue, and even `Val(x).value` is possible through `getpropery` overrides…

---

<div class="post-metadata">

**Author:** ![schlichtanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schlichtanders/32/32145_2.png) [@schlichtanders](https://discourse.julialang.org/u/schlichtanders)\
**Post date:** [October 17, 2023, 4:18pm UTC](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003/40 "2023-10-17T16:18:54Z")

</div>

My rule of thumb is that fields are implementation details of individual Types and don’t belong to generic APIs… but this is just gut feeling

(I guess I am thinking that it is harder to support a field based api instead of a function based api, maybe I am also afraid of performance penalties when using getproperty)

[Previous page](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003.md?page=1)

[Next page](https://discourse.julialang.org/t/unifying-unwrapping-single-value-wrappers/105003.md?page=3)
