# Dispatch checker

**URL:** <https://discourse.julialang.org/t/dispatch-checker/66191>\
**Category:** Performance\
**Tags:** question, type, dispatch\
**Created:** [August 11, 2021, 10:20am UTC](https://discourse.julialang.org/t/dispatch-checker/66191 "2021-08-11T10:20:43Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 11, 2021, 10:20am UTC](https://discourse.julialang.org/t/dispatch-checker/66191/1 "2021-08-11T10:20:44Z")

</div>

Having managed to express your _if typeof(v)_ by suitable dispatch calls, how do you check if they are correct or cover all possible cases?

Of course, like in other programming languages, one can always code test cases.  
But having some more dispatches or not 1-liners, it’s easy to overlook a case.

For logic circuit design there are tools to prove or visualize the implemented logic.  
Since the type system is huge but finite, I wonder if a clever type (…) has already thought of such an idea.

As a start,

```julia
fun(x::T1) = ...
fun(x::T2) = ...
fun(x) = ...

```

Q: How to find out what types the last (least specific) dispatch will catch?  
A: It catches all the rest.

Q: Could it happen that two dispatches overlap, with unintended consequences?  
A: Can happen, but watched by Julia’s ambiguous warning.

Q: Are there logic operations on types possible other than Union?  
A: (no)

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [August 11, 2021, 10:36am UTC](https://discourse.julialang.org/t/dispatch-checker/66191/2 "2021-08-11T10:36:15Z")

</div>

the answer is “everything else”.

---

<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:** [August 11, 2021, 10:37am UTC](https://discourse.julialang.org/t/dispatch-checker/66191/3 "2021-08-11T10:37:30Z")

</div>

> [@Bardo](#):
>
> ```julia
> fun(x) = ...
> 
> ```
> 
> How can you find out what types the last (least specific) dispatch will catch?

An untyped function argument is implicitly typed `Any`, so is able to be used with all types. If a different method with a more specific signature exists, it will be used instead, so it can be said that the function with the untyped argument will catch “everything that’s not more specific somewhere else”.

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 11, 2021, 10:42am UTC](https://discourse.julialang.org/t/dispatch-checker/66191/4 "2021-08-11T10:42:49Z")

</div>

You are both right of course, then the “total” question is solved 🙂  
Will edit title and add another question.

---

<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:** [August 11, 2021, 10:52am UTC](https://discourse.julialang.org/t/dispatch-checker/66191/5 "2021-08-11T10:52:51Z")

</div>

> [@Bardo](#):
>
> Could it happen that two dispatches overlap, with unintended consequences?

In that case, you’ll get an ambiguity error:

```julia
julia> f(a, b::Integer) = "1"                                    
f (generic function with 1 method)                               
                                                                 
julia> f(a::Integer, b) = "2"                                    
f (generic function with 2 methods)                              
                                                                 
julia> f(1,2)                                                    
ERROR: MethodError: f(::Int64, ::Int64) is ambiguous. Candidates:
  f(a, b::Integer) in Main at REPL[1]:1                          
  f(a::Integer, b) in Main at REPL[2]:1                          
Possible fix, define                                             
  f(::Integer, ::Integer)                                        
Stacktrace:                                                      
 [1] top-level scope                                             
   @ REPL[3]:1                                                   

```

> [@Bardo](#):
>
> Are there logic operations on types possible other than Union?

I’m not sure what you mean by that. Where do you want to use the result?

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 11, 2021, 11:05am UTC](https://discourse.julialang.org/t/dispatch-checker/66191/6 "2021-08-11T11:05:05Z")

</div>

Not needed here because of the built-in ambiguity check.  
For a general case, take

```julia
isstruct(obj::T) where {T} = !isprimitivetype(T)
isstruct(obj::Union{Array,Tuple,String}) = false

```

could then be expressed by something like

```julia
!isprimitivetype(T) without Union{Array,Tuple,String}
```

---

<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:** [August 11, 2021, 11:09am UTC](https://discourse.julialang.org/t/dispatch-checker/66191/7 "2021-08-11T11:09:02Z")

</div>

No, such exclusions are not possible as far as I know. It’s also extremely niche to need this in the first place - you’re basically saying “all types work for this function, except X, Y and Z”, which feels weird for the vast majority of cases. To me it’s more about a method saying “I can accept V, W and U, and nothing else”.

You’d also have to write the method for the others as well anyway, leading to repetition in type signatures (and worse, if you update one definition you’ll have to update the other as well, quickly leading to cross-cutting changes where none existed before).

On top of that, what if a new type comes into scope that also doesn’t work for your method? Would you have to update it as well?

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 11, 2021, 11:17am UTC](https://discourse.julialang.org/t/dispatch-checker/66191/8 "2021-08-11T11:17:34Z")

</div>

Makes sense, the two seemed to imply the same logic, but a type is general (global scope), the isstruct itself is placed in a (usually dispatched) function, thus being more selective. - Thx!

---

<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:** [August 11, 2021, 11:27am UTC](https://discourse.julialang.org/t/dispatch-checker/66191/9 "2021-08-11T11:27:58Z")

</div>

You can think of the julia type graph as having a `Top` type (of which all types are a subtype, this is `Any`) and a `Bottom` type (of which all types are a supertype, this is the empty `Union{}`). What you can do with dispatch is to select a given “path” from some type that’s `<: Any` (or whatever you specify in a signature) towards `Union{}`. If you select a union of types (i.e. `f(a::Union{A,B}`), you effectively allow multiple “paths” from subtypes of the elements of the `Union` to be used here. Note that `A` and `B` need not be concrete types! If they’re abstract types, all concrete subtypes of that abstract type are allowed (plus the empty Union, but no concrete object can have the empty union as its type).

Saying “except X” in that type graph would mean “All paths from `Any` to `Union{}`, but the path that begins at `X`” (which again feels a little weird - you’re effectively saying “no matter what you come up with, I can handle it”). At that point, you might as well use `::Any` and use specialized methods for all other things you want to handle differently (and not repeat yourself).
