# Tutorial on using advanced type system in Julia?

**URL:** <https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903>\
**Category:** New to Julia\
**Created:** [January 3, 2020, 1:11am UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903 "2020-01-03T01:11:16Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![yuxi.liu](https://avatars.discourse-cdn.com/v4/letter/y/839c29/32.png) [@yuxi.liu](https://discourse.julialang.org/u/yuxi.liu)\
**Post date:** [January 3, 2020, 1:11am UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/1 "2020-01-03T01:11:16Z")

</div>

Is there a tutorial on advanced use of types in Julia? The manual isn’t enough for me to read advanced abstract type usage in actual packages.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [January 3, 2020, 1:12am UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/2 "2020-01-03T01:12:58Z")

</div>

This one on type-dispatch designs might be helpful:

> **[Type-Dispatch Design: Post Object-Oriented Programming for Julia - Stochastic...](https://www.stochasticlifestyle.com/type-dispatch-design-post-object-oriented-programming-julia/)**
>
> In this post I am going to try to explain in detail the type-dispatch design which is used in Julian software architectures. It’s modeled after the design of many different packages and Julia Base, and has been discussed in parts elsewhere. This is...

---

<div class="post-metadata">

**Author:** ![tro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tro3/32/12355_2.png) [@tro3](https://discourse.julialang.org/u/tro3)\
**Post date:** [January 3, 2020, 3:35am UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/3 "2020-01-03T03:35:46Z")

</div>

Thanks for the link. Until reading that blog, I did not appreciate that type-checking within a function would get compiled away. (The optimization does not hit until @code\_llvm, and I’d been spending time in @code\_warntype mostly.)

So newbie question - given that it seems like both structures below will compile to the same point, which is considered the more Julian style?

```julia
function action(x::TypeA)
  [work on a TypeA]
end

function action(x::TypeB)
  [work on a TypeB]
end

or

function action(x)
  if x isa TypeA
    [work on a TypeA]
  elseif x isa TypeB # ignoring the wrong-type case to keep things short
    [work on a TypeB]
  end
end

```

I’ve been using the former, in part because it is self-documenting. But I’m curious about the community’s opinion. (I also now suspect I have been over-typing - I need to take a second look at my code…)

Thanks.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 3, 2020, 3:41am UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/4 "2020-01-03T03:41:23Z")

</div>

> [@tro3](#):
>
> So newbie question - given that it seems like both structures below will compile to the same point, which is considered the more Julian style?

The former: We generally use dispatch rather than explicit type-checks.

(If you grep the `Base` code for `" isa "` you’ll find a few exceptions to this rule, mainly in processing syntax trees and other [reflection](https://en.wikipedia.org/wiki/Reflection_(computer_programming)) code. Basically you use `isa` in cases where almost all of the code is the same for different types, so that you only need to tweak the behavior for a particular type, and it doesn’t seem worth the trouble to refactor.)

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [January 3, 2020, 5:05am UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/5 "2020-01-03T05:05:17Z")

</div>

> [@tro3](#):
>
> I’ve been using the former, in part because it is self-documenting. But I’m curious about the community’s opinion. (I also now suspect I have been over-typing - I need to take a second look at my code…)

The former also has the nice feature that, because the checking is done by dispatch, anyone can add new dispatches to further specialize the behavior in new ways. If you do if statements, the only way to add new branches is to change the function itself. This form of “extendability from outside” is often described as the multiple dispatch solution to the [expression problem](https://en.wikipedia.org/wiki/Expression_problem)

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [January 3, 2020, 9:38am UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/6 "2020-01-03T09:38:04Z")

</div>

> [@stevengj](#):
>
> Basically you use `isa` in cases where almost all of the code is the same for different types, so that you only need to tweak the behavior for a particular type, and it doesn’t seem worth the trouble to refactor.

In the case of handling ASTs (e.g., in Julia’s compiler internals), it’s less about “trouble to refactor” and more about performance. For code that is unavoidably non-inferrable (like most AST-handling code), the big cost is runtime method lookup. When types can’t be inferred, there are nevertheless two cases where the lookup can still be done at compile time:

- when a function has only one method
- inside the body of an `isa` conditional block, where the type is “locally” known

`isa` blocks contribute to both of these, since you can use them as a poor substitute for dispatch.

The obvious (and major) downside is that this is non-extensible without editing the original source code, so it’s best reserved for private interfaces.

---

<div class="post-metadata">

**Author:** ![yuxi.liu](https://avatars.discourse-cdn.com/v4/letter/y/839c29/32.png) [@yuxi.liu](https://discourse.julialang.org/u/yuxi.liu)\
**Post date:** [January 3, 2020, 2:49pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/7 "2020-01-03T14:49:05Z")

</div>

I have two versions of a function that deals with two different types of inputs, and they differ only in a few crucial lines. It would be very easy to use `isa` conditional block to write it, and it would avoid repeating the code, but it seems you would recommend keeping theme two separate functions and repeat the code?

---

<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:** [January 3, 2020, 3:15pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/8 "2020-01-03T15:15:10Z")

</div>

I don’t think that anyone was advocating repeating code explicitly.

It is hard to give specific advice without concrete code, but I would first factor out the common parts to small pieces, make them `@inline` if they don’t, then use `isa` branches only when absolutely necessary.

Again, this is probably unnecessary micro-optimization for 90% of real-life code. And of course the remaining 10% soaks up 90% of the developer and runtime. 😉

BTW, this has little to do with the advanced usage of the type system _per se_, it is more about working around limitations of inference.

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [January 3, 2020, 8:10pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/9 "2020-01-03T20:10:58Z")

</div>

Something to keep in mind when coming to Julia from a language like Python is that Julia has very little function call overhead as long as the compiler is able to figure out in advance what the types will be. It’s perfectly fine to write lots of small functions instead of one big one (which might require un-learning some Python habits.) You will frequently see code like this:

```julia
function f(x)
   while in_hot_loop
       g(y)
   end
end
g(y::TypeA) = something
g(y::TypeB) = something_else

```

---

<div class="post-metadata">

**Author:** ![Olof\_Salberger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/olof_salberger/32/4850_2.png) [@Olof\_Salberger](https://discourse.julialang.org/u/Olof_Salberger)\
**Post date:** [January 3, 2020, 11:14pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/10 "2020-01-03T23:14:37Z")

</div>

So isa conditional blocks are like smart casts in Kotlin, and the compiler can actually use the type information from them?

In that case, as a microoptimization stategy, does that mean it can be worth it to sprinkle something like

```julia
@regardlessof (x isa CommonType) foo(x,otherargs...)

```

instead of a plain call to foo(x,otherargs…) , where the convenience macro @regardlessof(cond,expr) desugars to cond ? expr : expr .

Would it speed up code in the case where one dispatch is overwhelmingly more common?

---

<div class="post-metadata">

**Author:** ![dpsanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dpsanders/32/3573_2.png) [@dpsanders](https://discourse.julialang.org/u/dpsanders)\
**Post date:** [January 4, 2020, 12:12am UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/11 "2020-01-04T00:12:13Z")

</div>

I tried to make a tutorial introduction here (given at JuliaCon 2019):

> **[GitHub - dpsanders/intermediate\_julia\_2019](https://github.com/dpsanders/intermediate_julia_2019)**
>
> Contribute to dpsanders/intermediate\_julia\_2019 development by creating an account on GitHub.

---

<div class="post-metadata">

**Author:** ![tro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tro3/32/12355_2.png) [@tro3](https://discourse.julialang.org/u/tro3)\
**Post date:** [January 4, 2020, 1:01am UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/12 "2020-01-04T01:01:54Z")

</div>

> [@Per](#):
>
> which might require un-learning some Python habits

🙂 Actually, I am coming from a Python-heavy (for task management) and Haskell-lite (for number crunching) background. I am loving that Julia appears poised to handle both fronts, but I have things to unlearn on both sides.

The bit that escaped me until this thread was the fact that typeof, eltype, etc were handle-able at compile time. I now see things like (simple example)

```julia
mapperfunc(fn, itr) = mapperfunc(eltype(itr), fn, itr)

function mapperfunc(::Type{T}, fn, itr) where T
  ...
end

```

inferring to the correct output type for `mapperfunc(x->2x, [1,2])`. (Previously, I would have read `eltype` as a purely runtime function.) Very cool. Thanks, all

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [January 4, 2020, 11:00am UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/13 "2020-01-04T11:00:03Z")

</div>

That’s not necessary or even helpful when types can be inferred, because in that case the dispatch can be “hard wired” during compilation (reducing the cost of dispatch literally to zero). When types can’t be inferred, it still might not be helpful, because the compiler does some of this manually (see [union-splitting](https://julialang.org/blog/2018/08/union-splitting)). However, to prevent compilation from taking unbounded time, in sufficiently complicated cases the compiler will decide not to do union-splitting; only in those cases can this kind of “manual dispatch” be helpful.

Example:

```julia
struct A end
struct B end
struct C end
struct D end
struct E end

f(::A) = 1
f(::B) = 2
f(::C) = 3
f(::D) = 4
f(::E) = 5

function dispatch(list)
    s = 0
    @inbounds for item in list # @inbounds not necessary (it's just to simplify the result of `@code_warntype dispatch(list)`)
        if item isa A
            s += f(item) # this f gets compile-time dispatched (and inlined, since it's simple)
        else
            s += f(item)::Int # this call to f is compile-time dispatched if `item` is inferrable, runtime otherwise
        end
    end
    return s
end

lista_inf = [A() for i = 1:10]
lista_any = Any[A() for i = 1:10]
listc_inf = [C() for i = 1:10]
listc_any = Any[C() for i = 1:10]

using BenchmarkTools
@btime dispatch($lista_inf)
@btime dispatch($lista_any)
@btime dispatch($listc_inf)
@btime dispatch($listc_any)

```

Results:

```julia
julia> include("/tmp/dispatch.jl")
  2.514 ns (0 allocations: 0 bytes)
  16.515 ns (0 allocations: 0 bytes)
  2.234 ns (0 allocations: 0 bytes)
  101.940 ns (0 allocations: 0 bytes)
30

```

You can see the performance on non-inferred `A` objects is considerably better than the performance on non-inferred `C` objects, but that when inference works then none of this matters.

You can convince yourself that there’s a threshold of 5 total (4 runtime-dispatched) methods for this bad performance to kick in. If you didn’t have `E`, then automatic union-splitting would make the second `f` call pretty much like the first one.

---

<div class="post-metadata">

**Author:** ![tro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tro3/32/12355_2.png) [@tro3](https://discourse.julialang.org/u/tro3)\
**Post date:** [January 4, 2020, 3:16pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/14 "2020-01-04T15:16:02Z")

</div>

> [@tro3](#):
>
> The bit that escaped me until this thread was the fact that typeof, eltype, etc were handle-able at compile time. I now see things like (simple example)
> 
> ```julia
> mapperfunc(fn, itr) = mapperfunc(eltype(itr), fn, itr)
> 
> function mapperfunc(::Type{T}, fn, itr) where T
> ...
> end
> 
> ```
> 
> inferring to the correct output type for `mapperfunc(x->2x, [1,2])` . (Previously, I would have read `eltype` as a purely runtime function.) Very cool. Thanks, all

Hmm - now I see I am still a little off. `eltype` allows a compile-time inference on an iterable, but how is the proper output inference done for, say, `map`? The `eltype` of a function, even with an output type, appears to be `Any` and `typeof(fn)` appears to generate only runtime variables. How does one (or even can one) infer the output type of a function at compile time? I assume given that `map` does it, it is possible…

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [January 4, 2020, 3:44pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/15 "2020-01-04T15:44:50Z")

</div>

Each function has its own type, so if you call, for example, `map(sin, [1,2,3])`, compilation and type inference is performed for `map(::typeof(sin), ::Vector{Int})` (and not something like `map(::Function, ::Vector{Int})`, which would be too general.)

A function doesn’t have an `eltype` per se, but the compiler will figure out that `sin` will be called with an `Int` each time, and that the return type is always `Float64` in that case.

---

<div class="post-metadata">

**Author:** ![tro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tro3/32/12355_2.png) [@tro3](https://discourse.julialang.org/u/tro3)\
**Post date:** [January 4, 2020, 4:09pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/16 "2020-01-04T16:09:55Z")

</div>

Thanks. When I look at @code\_warntype using typeof(fn), I don’t get inferred types, I get var"#321#322" looking things (I think this what you meant by “Each function has its own type”), which I assume won’t infer as the output? The (dumb) test case below shows what I am targeting but of course won’t run:

```julia
julia> test(fn, x) = test(fn, x, typeof(fn))
test (generic function with 1 method)

julia> function test(fn, x, ::Type{T}) where T
         result = T[]
         push!(result, fn(x))
         return result
       end
test (generic function with 2 methods)

```

I guess to summarize the question - if I want to pre-allocate memory to store the output of a passed function (not quite what I’m doing above, but you get the idea) and have the outer function return type infer as that storage, how do I go about getting that storage type at compile time? Thanks.

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [January 4, 2020, 5:16pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/17 "2020-01-04T17:16:43Z")

</div>

Often something like `Vector{typeof(f(zero(eltype(x))))}(undef,N)` will work, but this may result in `f(0)` actually being called. Especially if `f` has side effects. Also, if `f(0)` returns a different type than `f(1)`, there is obviously going to be problems.

I usually rely on functions like `map` to do the allocation for me. If I want to save allocations, I can use `map!` the second time around (with the array returned by `map`).

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [January 4, 2020, 5:28pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/18 "2020-01-04T17:28:41Z")

</div>

The problem with your example is that you are creating an array that can store the function `fn` - not its output. What you probably want to do is simply this.

```julia
test(fn, x) = [fn(x)]

```

For this, the output type (`Array{Float64}`) is computed at compile-time.

---

<div class="post-metadata">

**Author:** ![tro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tro3/32/12355_2.png) [@tro3](https://discourse.julialang.org/u/tro3)\
**Post date:** [January 4, 2020, 5:35pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/19 "2020-01-04T17:35:56Z")

</div>

🙂 Like I said, a dumb example. A more complete one would have had the `push!` be a threaded loop, dumping the output in `result`.

Sounds like it is nontrivial to force what I’m looking for, but it is probably just a question of getting used to the inference model. Thanks for the help.

---

<div class="post-metadata">

**Author:** ![tro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tro3/32/12355_2.png) [@tro3](https://discourse.julialang.org/u/tro3)\
**Post date:** [January 4, 2020, 5:56pm UTC](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903/20 "2020-01-04T17:56:43Z")

</div>

Total hack, but this seems to work…

```julia
julia> fn = x::Int->x/2
#32 (generic function with 1 method)

julia> eltype(typeof(map(fn, [])))
Float64

```

🙂 Seems like there must be an easier way.

[Next page](https://discourse.julialang.org/t/tutorial-on-using-advanced-type-system-in-julia/32903.md?page=2)
