# Question about methods defined for supertypes

**URL:** https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343
**Category:** General Usage
**Created:** [June 16, 2019, 10:44am UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343 "2019-06-16T10:44:46Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 10:44am UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/1 "2019-06-16T10:44:46Z")

</div>

I am still new to Julia (about a year in and I am only starting to get it), so please bear with me.

So I was wondering what I could do with the type `Base.KeySet` which has supertype `AbstractSet{K} where K`. Currently, methods defined for `Base.KeySet` seems to be limited to

```julia
in(k, v::Base.KeySet) in Base at abstractdict.jl:63

```

Ok, fine. However, `AbstractSet{K} where K` has other methods defined for, such as:

```julia
setdiff!(s::AbstractSet, itr) in Base at abstractset.jl:167

```

In my head, I figured the methods defined for the supertype would also work for the methods defined for the subtype. However, the method above, does not work for type `Base.KeySet`.

I suppose I still have not quite gotten yet how types are meant to work in Julia, but I expected that `Base.KeySet` would behave as a `AbstractSet{K} where K` where behaviour is defined over methods, is this not correct?  
Pedro

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 16, 2019, 11:01am UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/2 "2019-06-16T11:01:15Z")

</div>

First, `KeySet` is an implementation detail, not an exposed interface (it is neither exported nor documented). It is used to implement `keys`. It is very unlikely that you need to be using it outside `Base.`

Second,

> [@phrmoy](#):
>
> methods defined for the supertype would also work for the methods defined for the subtype

is not the right way to think about it. In absence of a more specific method, methods for the supertype can be _called_, but that does not mean they will _work_. Whether that is a bug depends on this behavior being part of the interface. Here, `setdiff!` fails because the first argument is not mutable (and it should not be).

If you want to modify a list of keys, use something like

```julia
k = Set(keys(a))

```

and work on that.

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 11:19am UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/3 "2019-06-16T11:19:17Z")

</div>

Thank you, for your reply. I am not sure what it means for `Base.KeySet` to be an implementation detail, but I do work with the instance of the type in my code, and for all intent and purposes, it’s just an instance of a type, like all others. Though we are talking specifically about `Base.KeySet`, we don’t need to, but for the sake of the question, if I wanted to write a function such as

```julia
function remove_default(xs::AbstractSet)::Nothing
  setdiff!(xs,(0,))
  return nothing
end

```

I would not be able to call `remove_default(keys(Dict(0=>0,1=>1)))`. I get that for this specific case, you suggested I transform the instance into a new instance of type `Set`. But that’s not the point, my question is about the relationship between the subtype and the supertype. It would feel natural for the subtype to behave like the supertype, so we can write functions for the supertype, and pass in the subtype.

Now, to make matters worse, in this specific case, some methods of `AbstractSet` are defined for `KeySet`, so now, how do I consistently know what works, and what does not? I mean, if `KeySet` does not behave like a `AbstractSet`, in this specific case, why would it be a subtype of `AbstractSet`?

Except, of course, that I might not be getting this right as you suggested, that subtypes and supertypes are not meant to behave the same. But I would very much like to think that they should.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 16, 2019, 11:28am UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/4 "2019-06-16T11:28:18Z")

</div>

> [@phrmoy](#):
>
> I would not be able to call `remove_default(keys(Dict(0=>0,1=>1)))` .

Why? I don’t see what prevents this. **EDIT** you would either need to make it use a functional implementation, or `collect` the argument though.

> [@phrmoy](#):
>
> It would feel natural for the subtype to behave like the supertype, so we can write functions for the supertype, and pass in the subtype.

Not all subtypes of `AbstractSet` are _mutable_ containers. Your code simply makes an erroneous assumption about the argument. Either the caller makes sure that the argument is mutable, or the function won’t work.

> [@phrmoy](#):
>
> some methods of `AbstractSet` are defined for `KeySet` , so now, how do I consistently know what works, and what does not?

It is true that the interface for `AbstractSet` is not formally documented. But the real issue here whether some container is mutable or not. I am not aware of a way to query this in general (cf `applicable` though).

In this specific case, I would define a method using `setdiff` (note absence of `!`) for the _generic_ case, and perhaps use `setdiff!` for some special cases I know that support it (`Set` comes to mind).

Also, you should be [using `!` for all function names that modify an argument](https://docs.julialang.org/en/v1/manual/style-guide/#Append-!-to-names-of-functions-that-modify-their-arguments-1).

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 11:49am UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/5 "2019-06-16T11:49:23Z")

</div>

Fair enough, redefining my function:

```julia
function remove_default!(xs::AbstractSet)::Nothing
  setdiff!(xs,(0,))
  return nothing
end

```

So the call `remove_default!(keys(Dict(0=>0,1=>1)))` would fail obviously because I used the method `setdiff!` defined for `AbstractSet` that does not work for `KeySet` as you noted that containers may or not may be mutable.

Let me give this one example, if I have the function signature as given below

```julia
function add_something(x::Number)

```

I would expect that I can pass any subtype of `Number` to the method above and get back a well-defined response. Agreed?

For `AbstractSet`, we cannot accomplish that, assuming this was intended to be this way. And I don’t see why these cases should be different.

If `KeySet` is an immutable container, then shouldn’t it be a subtype of some immutable abstract type? I expect `Int` to always behave like a `Number`, always. By the same token, I would also expect `KeySet` to always behave as a `AbstractSet`, always (if that is how a `KeySet` is supposed to behave like indeed).

Is this not the correct way to think about it? Would it not make sense for instance, in this specific case, to have a supertype `ImmutableAbstractSet` having `KeySet` as a subtype such that `KeySet` behaves like a `ImmutableAbstractSet`, always (assuming we are going into differences between mutable and immutable containers)?

At the moment, what I am walking away with, is that the supertype does not define behavior for the subtype, then I am not sure what the contract is between the subtype and the supertype.

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 11:58am UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/6 "2019-06-16T11:58:53Z")

</div>

> [@Tamas\_Papp](#):
>
> Your code simply makes an erroneous assumption about the argument

By the way, the code makes no erroneous assumption about the argument, it passes a subtype of the type `AbstractSet`, no assumptions made (I shouldn’t need to know the internals of the function, I mean, you don’t go around the Base source code to inspect internals of methods before you call them, do you?). Example, you look up the documentation and find:

```julia
sort!(v::AbstractArray{T,1} where T, lo::Int64, hi::Int64, a::Base.Sort.MergeSortAlg, o::Base.Order.Ordering, t) in Base.Sort at sort.jl:544

```

Now, you know how to call the function right? You don’t need to go inspect the source code to know how it works. The types tell you what it’s acceptable, that’s the contract. So you can call the function without understanding its implementation details.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 16, 2019, 12:03pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/7 "2019-06-16T12:03:57Z")

</div>

> [@phrmoy](#):
>
> no assumptions made

Your assumption is that the argument is mutable. Cf

```julia
julia> a = 1:2
1:2

julia> a[1] = 3
ERROR: setindex! not defined for UnitRange{Int64}
Stacktrace:
 [1] error(::String, ::Type) at ./error.jl:42
 [2] error_if_canonical_setindex(::IndexLinear, ::UnitRange{Int64}, ::Int64) at ./abstractarray.jl:1082
 [3] setindex!(::UnitRange{Int64}, ::Int64, ::Int64) at ./abstractarray.jl:1073
 [4] top-level scope at REPL[17]:1

```

even though

```julia
julia> typeof(a) <: AbstractVector
true

```

> [@phrmoy](#):
>
> Would it not make sense for instance, in this specific case, to have a supertype `ImmutableAbstractSet` having `KeySet` as a subtype such that `KeySet` behaves like a `ImmutableAbstractSet` , always (assuming we are going into differences between mutable and immutable containers)?

This could make sense, but I would very much prefer a trait for this. Branching each type would be tedious. OTOH, `applicable` would work as I suggested above (at no runtime cost).

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 12:11pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/8 "2019-06-16T12:11:19Z")

</div>

Alright, yeah so the supertype does not tell the whole story. Oh well, trial and error it is then;)

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 16, 2019, 12:20pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/9 "2019-06-16T12:20:53Z")

</div>

> [@phrmoy](#):
>
> Oh well, trial and error it is then;)

I think you keep missing the suggestion about `applicable`.

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 12:24pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/10 "2019-06-16T12:24:26Z")

</div>

Thank you for pointing that out again.

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 12:31pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/11 "2019-06-16T12:31:24Z")

</div>

And that still leaves us with trial and error by the way.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 16, 2019, 12:46pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/12 "2019-06-16T12:46:22Z")

</div>

I think you misunderstand. It gives you a way to query without trying, _at compile time_. Cf

```julia
abstract type A end
struct B <: A end
struct C <: A end
f(::A) = :general
f!(::C) = :specific
g(x::A) = _g(Val{applicable(f!, x)}(), x)
_g(::Val{false}, x) = f(x)
_g(::Val{true}, x) = f!(x)
g(B()) # :general
g(C()) # :specific

```

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 12:56pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/13 "2019-06-16T12:56:52Z")

</div>

That’s pretty neat, thank you.  
From a function designer’s perspective, someone providing the API, that’s great.  
Now, from a user’s perspective, someone attempting to use the API, what you’re saying is that, when in doubt, we could cover both cases when a container may or may not be mutable? I mean, that’s certainly a solution. It feels like a bit more work than just trying `applicable` on the REPL for some specific use-case (trial and error) and moving on (as a user). Either way, we will still be trying either in an automated or manual fashion.

---

<div class="post-metadata">

### Author: ![kevbonham](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kevbonham/32/216165_2.png) [@kevbonham](https://discourse.julialang.org/u/kevbonham)
#### Post date: [June 16, 2019, 1:21pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/14 "2019-06-16T13:21:24Z")

</div>

> [@phrmoy](#):
>
> Now, from a user’s perspective, someone attempting to use the API, what you’re saying is that, when in doubt, we could cover both cases when a container may or may not be mutable?

Well, from a user’s perspective, you should be relying on the documented API. This is what Tamas was pointing to up at the top when he said `KeySet` is an implementation detail and not documented or exported. The implication is that you should not necessarily expect it to be completely fleshed out. And crucially, you should not expect it to be maintained with the same functionality going forward.

Yes, in an ideal world, package authors would make sure that (documented and exported) things that inherit from abstract types have the behaviors associated with the parent type, but there’s nothing in principle stopping me from declaring

```julia
struct Foo <: AbstractVector end

```

Though basically no methods defined in `AbstractVector`s will work on it.

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 1:39pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/15 "2019-06-16T13:39:37Z")

</div>

Thanks, that does clear things up a bit on the expectation front. I was just a bit surprised given this one example, used at the beginning of the thread, is coming from Base (not an external package so I had a slightly different expectation of consistency for Base code), certainly much more so understandable from an external package’s perspective. None the less, I am still coming to terms that subtypes and supertypes are not required to behave the same. Maybe it’s just a wrong notion after all from other programming paradigms that I am trying to relay on top of Julia.

---

<div class="post-metadata">

### Author: ![kevbonham](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kevbonham/32/216165_2.png) [@kevbonham](https://discourse.julialang.org/u/kevbonham)
#### Post date: [June 16, 2019, 2:14pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/16 "2019-06-16T14:14:27Z")

</div>

> [@phrmoy](#):
>
> I was just a bit surprised given this one example, used at the beginning of the thread, is coming from Base (not an external package so I had a slightly different expectation of consistency for Base code)

I think that’s a reasonable assumption in general, but undocumented is undocumented. All bets are off with something used internally. Julia doesn’t have the ability to declare “private” methods and types. But for things that are not exported, you should use at your own risk.

> [@phrmoy](#):
>
> Maybe it’s just a wrong notion after all from other programming paradigms that I am trying to relay on top of Julia.

Perhaps. I think of abstract types less like a parent class in Python and more like an opportunity to make things generic. If I want a function that returns the square of the sum of two arguments, I can declare

```julia
squaresum(a::Real, b::Real) = (a+b)^2

```

Rather than having to write a method for Floats and Ints and everything else. Of course, someone might come along and declare a type that’s a subtype of Number but fail to define `:+()`, in which case there’s nothing my method can do.

Another way to think of it - I could have instead written

```julia
squaresum(a::Any, b::Any) = (a+b)^2

```

All things are subtypes of `Any`, would you expect every type to make sure it could be called by `squaresum`?

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 2:30pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/17 "2019-06-16T14:30:20Z")

</div>

> [@kevbonham](#):
>
> All things are subtypes of `Any` , would you expect every type to make sure it could be called by `squaresum` ?

No. I would simply expect `squaresum` to work for all types subtyped by `Any`. Clearly, you defined it in a way that it won’t work by employing an operator which does not work for all types that subtype `Any`, that is a violation of the contract you just set forth for your method (a design error). Now, if your function definition used methods that are supposed to work on all types that subtype `Any`, and I pass an object with a type that subtypes `Any` and it fails, this is where we have the discrepancy which we have been discussing.

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 2:35pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/18 "2019-06-16T14:35:29Z")

</div>

> [@kevbonham](#):
>
> Of course, someone might come along and declare a type that’s a subtype of Number but fail to define `:+()` , in which case there’s nothing my method can do.

Right, this is the issue. And there shouldn’t be anything for your method to do, as long as you honored the contract of your method signature (the burden is not on your method).

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 16, 2019, 4:40pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/19 "2019-06-16T16:40:52Z")

</div>

I think you may be coming from a language with more formalized interfaces (C++?). Julia in theory could follow that path, but in practice most often it doesn’t: interfaces grow organically, may not have a formal definition until [mature](https://docs.julialang.org/en/v1/manual/interfaces/), and it is considered perfectly acceptable to just write a generic method and have it fail when called with the wrong types (cf [duck typing](https://en.wikipedia.org/wiki/Duck_typing)).

You may want to just embrace this style of programming for a while in order to learn Julia better.

---

<div class="post-metadata">

### Author: ![phrmoy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/phrmoy/32/8912_2.png) [@phrmoy](https://discourse.julialang.org/u/phrmoy)
#### Post date: [June 16, 2019, 4:43pm UTC](https://discourse.julialang.org/t/question-about-methods-defined-for-supertypes/25343/20 "2019-06-16T16:43:28Z")

</div>

Thanks, I appreciate the feedback.
