# Letting a package use external user-created functions

**URL:** <https://discourse.julialang.org/t/letting-a-package-use-external-user-created-functions/52345>\
**Category:** General Usage\
**Created:** [December 24, 2020, 9:17pm UTC](https://discourse.julialang.org/t/letting-a-package-use-external-user-created-functions/52345 "2020-12-24T21:17:20Z")\
**Posts on this page:** 7\
**Page:** 2

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [December 26, 2020, 7:32pm UTC](https://discourse.julialang.org/t/letting-a-package-use-external-user-created-functions/52345/21 "2020-12-26T19:32:07Z")

</div>

The performance problem comes when a loop like

```julia
for p1 in v1
  for p2 in v2
     compute(p1,p2)
  end
end

```

Has to call different methods of `compute` at each iteration. That will happen if the type of `p1` or the type of `p2` change from iteration to iteration. That should be avoided.

---

<div class="post-metadata">

**Author:** ![MA\_Laforge](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ma_laforge/32/385_2.png) [@MA\_Laforge](https://discourse.julialang.org/u/MA_Laforge)\
**Post date:** [December 26, 2020, 7:55pm UTC](https://discourse.julialang.org/t/letting-a-package-use-external-user-created-functions/52345/22 "2020-12-26T19:55:49Z")

</div>

I know @lmiq answered, but I find a few details are missing.

> [@Michael\_Wang](#):
>
> where the dispatch function is simply
> 
> ```julia-auto
> function compute_force(p_i::T1, p_j::T2) where {T1 <: AbstractParticle, T2 <: AbstractParticle}
> compute stuff for p_i, p_j
> end
> 
> ```
> 
> Will this function still work if I user defines `CustomParticle <: AbstractParticle` outside the package?

I agree with @lmiq. Yes this is the way to go.

### Simpler function signature

If you do this, you don’t need to explicitly tell Julia that the vectors might have different element types in your function signature. Julia will _ **automatically** _ generate specialized versions of `compute_interaction` for those vectors, and will _ **probably** _ inline your functions! If not, well, you end up paying the cost of a function call - but that’s not incredibly expensive:

```julia-auto
function compute_interaction(particles_i::Vector, particles_j::Vector)
    for p_i in particles_i, p_j in particles_j
        p_i.force += compute_force(p_i, p_j)
    end
end

```

~~Though I question the practicality of having a `force` accumulator in the `Particle` object itself because it might get messy to track.~~

_ **EDIT:** _ Maybe I should withdraw this previous statement. There is something somewhat natural about this representation. It might actually work out, if done well.

I will point out that I highly suggest you _ **at least** _ specify that `particles_i::Vector` & `particles_i::Vector` are `Vector`-s - unlike what @lmiq suggested:

> [@lmiq](#):
>
> Yet, just know that if you do nor specify at all the input types everything will work as well:
> 
> `function compute_interaction(particles_i, particles_j)`

This definition would try to trap _ **all** _ two-argument calls to `compute_interaction` (but your implementation clearly expects `Vector`-s).

### More reading: Function Barriers

I think this is what Julia developers call function barriers. I have read discussions about them, but I can’t find any of them at this time.

There is a mention of function barriers here:  
 → [Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/#kernel-functions)

But they are examples of explicit barriers, as opposed to being implicitly created through function calls.

If someone reading this knows where a proper discussion of this design pattern is found, please include it in this thread.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [December 26, 2020, 8:02pm UTC](https://discourse.julialang.org/t/letting-a-package-use-external-user-created-functions/52345/23 "2020-12-26T20:02:14Z")

</div>

> [@MA\_Laforge](#):
>
> I will point out that I highly suggest you _ **at least** _ specify that `particles_i::Vector` & `particles_i::Vector`

I will almost agree here, except that you probably want `AbstractVector`, because then you accept views. (edit: and static arrays)

---

<div class="post-metadata">

**Author:** ![MA\_Laforge](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ma_laforge/32/385_2.png) [@MA\_Laforge](https://discourse.julialang.org/u/MA_Laforge)\
**Post date:** [December 26, 2020, 8:19pm UTC](https://discourse.julialang.org/t/letting-a-package-use-external-user-created-functions/52345/24 "2020-12-26T20:19:51Z")

</div>

> [@Michael\_Wang](#):
>
> Just to be clear, `function compute_interaction(particles_i, particles_j)` will perform fine as long as I don’t operate on the vectors `particles_i` and `particles_j` within this function and instead dispatch depending on the types of each element in the for loop? Or is there a performance degradation for simply looping over abstract types?

Not exactly. What matters is whether or not Julia can tell what’s in each _ **cell** _ of those vectors. This is when I try to pretend I am the Julia compiler (or whatever we call the component that deals with this logic):

### When Julia can’t anticipate

If `particles_i` is a `Vector{AbstractParticle}`, and `particles_j` is a `Vector{ConcreteParticle1}` then, for each loop iteration:

- Juila doesn’t know what type of particle will be in `particles_i[i]`.
- Even if Juila _ **does** _ know that `particles_j[j]` is of type `ConcreteParticle1`.

…So _ **for each iteration** _, Julia has to do something like:

```julia-auto
if isa(particles_i[i], ConcreteParticle1)
    compute_force(particles_i[i]::ConcreteParticle1, particles_j[j]::ConcreteParticle1)
elseif isa(particles_i[i], ConcreteParticle2)
    compute_force(particles_i[i]::ConcreteParticle2, particles_j[j]::ConcreteParticle1)
elseif isa(particles_i[i], ConcreteParticle3)
    ...
end

```

And this is where you loose performance. Compute pipelines are broken when the compiler can’t predict what data it will encounter (or calls it will need to perform).

### When Julia CAN anticipate

On the other hand, if Julia KNOWS that `particles_i` is a `Vector{ConcreteParticle2}`, and `particles_j` is a `Vector{ConcreteParticle1}`, then it knows exactly which version of the code to execute:

- Juila _ **does** _ know that `particles_i[i]` is of type `ConcreteParticle2`.
- AND Juila _ **does** _ know that `particles_j[j]` is of type `ConcreteParticle1`.

so it just needs to _ **always** _ run the following:

```julia-auto
compute_force(particles_i[i]::ConcreteParticle2, particles_j[j]::ConcreteParticle1)

```

(for this particular call to `compute_interaction` with known, concrete vector types)

No more if statements! No more broken pipelines!

---

<div class="post-metadata">

**Author:** ![MA\_Laforge](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ma_laforge/32/385_2.png) [@MA\_Laforge](https://discourse.julialang.org/u/MA_Laforge)\
**Post date:** [December 26, 2020, 8:28pm UTC](https://discourse.julialang.org/t/letting-a-package-use-external-user-created-functions/52345/25 "2020-12-26T20:28:25Z")

</div>

> [@lmiq](#):
>
> I will almost agree here, except that you probably want `AbstractVector` , because then you accept views.

Very true. I have a mental block with using `AbstractVector`, partly because I avoid using it. I think the reason I avoid `AbstractVector` so much is that it keeps me from writing:

```julia
DoSomething(::Vector) = ... #Default behaviour
DoSomething(::Vector{ConcreteType1}) = ...
DoSomething(::Vector{ConcreteType2}) = ...

```

And forces me to ALWAYS use the more verbose where notation:

```julia
DoSomething(::AbstractVector{T}) where T<: AbstractType = ... #Default behaviour
DoSomething(::AbstractVector{T}) where T<: ConcreteType1} = ...
DoSomething(::AbstractVector{T}) where T<: ConcreteType1} = ...

```

Which I find makes the method definitions harder to read.

…But that doesn’t mean I am right in doing so.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [December 26, 2020, 9:44pm UTC](https://discourse.julialang.org/t/letting-a-package-use-external-user-created-functions/52345/26 "2020-12-26T21:44:39Z")

</div>

> [@MA\_Laforge](#):
>
> And forces me to ALWAYS use the more verbose where notation:

I am totally with you. I don’t like that either, and in functions that I had to accept views I finally ended many times removing completely the annotations.

---

<div class="post-metadata">

**Author:** ![MA\_Laforge](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ma_laforge/32/385_2.png) [@MA\_Laforge](https://discourse.julialang.org/u/MA_Laforge)\
**Post date:** [December 27, 2020, 9:40pm UTC](https://discourse.julialang.org/t/letting-a-package-use-external-user-created-functions/52345/27 "2020-12-27T21:40:45Z")

</div>

Just out of curiosity @Michael_Wang, did you know about `NBodySimulator.jl`?:  
[https://github.com/SciML/NBodySimulator.jl](https://github.com/SciML/NBodySimulator.jl)

I just stumbled onto this recently. I don’t fully appreciate why this is under the “machine learning” umbrella, but this looks somewhat similar to what you are describing.

[Previous page](https://discourse.julialang.org/t/letting-a-package-use-external-user-created-functions/52345.md?page=1)
