# Difference between function that accepts abstract types and another that uses type parameters

**URL:** <https://discourse.julialang.org/t/difference-between-function-that-accepts-abstract-types-and-another-that-uses-type-parameters/61687>\
**Category:** General Usage\
**Created:** [May 23, 2021, 4:52pm UTC](https://discourse.julialang.org/t/difference-between-function-that-accepts-abstract-types-and-another-that-uses-type-parameters/61687 "2021-05-23T16:52:30Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![anon60034542](https://avatars.discourse-cdn.com/v4/letter/a/90ced4/32.png) [@anon60034542](https://discourse.julialang.org/u/anon60034542)\
**Post date:** [May 23, 2021, 4:52pm UTC](https://discourse.julialang.org/t/difference-between-function-that-accepts-abstract-types-and-another-that-uses-type-parameters/61687/1 "2021-05-23T16:52:30Z")

</div>

Consider the following code:

```julia
function do_work(first::Number,second::Number)  
    println("Working with $(typeof(first)) and $(typeof(second))")
end 

function do_work(first::A,second::B) where {A<:Number, B<:Number}
    println("Working with $A and $B")
end 

```

1. Is there any difference between these two methods?
2. Should I favour the use of the parametric method over the method that accepts the abstract types? I guess it depends on the type of work I want to do, as pointed out in an [answer](https://discourse.julialang.org/t/elaboration-on-parametric-methods/61531/3) to my previous question, you can express certain relationships that you can’t with multiple dispatch.

---

<div class="post-metadata">

**Author:** ![heliosdrm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/heliosdrm/32/3851_2.png) [@heliosdrm](https://discourse.julialang.org/u/heliosdrm)\
**Post date:** [May 23, 2021, 7:00pm UTC](https://discourse.julialang.org/t/difference-between-function-that-accepts-abstract-types-and-another-that-uses-type-parameters/61687/2 "2021-05-23T19:00:48Z")

</div>

1. No, both are equivalent.
2. The first version is easier to read in my eyes. But as said in the answer you linked, and you correctly guess, in other circumstances you might need to use a definition more like the second one.

---

<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:** [May 24, 2021, 4:35am UTC](https://discourse.julialang.org/t/difference-between-function-that-accepts-abstract-types-and-another-that-uses-type-parameters/61687/3 "2021-05-24T04:35:54Z")

</div>

### Rule of thumb

@anon60034542, here is the rule of thumb I think you are seeking:

- If Julia can tell the types of all parameters passed as a function argument, it will auto-compile specialized code for that permutation of types.
  - This specialized code should typically execute relatively fast
  - It doesn’t really matter which notation you use for the function signature.

- If Julia _ **can not** _ tell what types are in those data structures, then Julia will have to execute _ **run-time** _ checks to execute the desired/required code.
  - It is these types of run-time checks that tend to slow things down in most dynamic languages.

So, as a rule of thumb, you should use whatever function signature is the simplest to read/write.

- I agree with @heliosdrm: I find the first signature is the easiest to read - but then the `print()` statement is slightly easier to read in the second function definition.
- I therefore suggest you use your professional judgment in choosing the how to write your function.

### Array/vector ambiguity

A typical case where Julia can’t easily tell what data is passed in on a function call is when you have arrays/vectors of abstract types:

```julia-auto
function f(xvec)
   return Float64[x*x for x in xvec]
end

myReal = Real[1,3,4,9]
myFloat64 = Float64[1,3,4,9]

#Executes sub-optimal version of f() since Julia has to check
#element type for the multiplication @ each iteration:
f(myReal)

#Executes more optimal implementation of f() since Julia *knows*
#type of each element involved in the operation:
f(myFloat64)

```

If you don’t want users to accidentally call sub-optimal versions of `f()`, you can choose to only define the specialized version:

```julia-auto
#This version won't work on Real[] arrays - so users will be forced to make a Float64[].
function f(xvec::Vector{Float64})
   return [x*x for x in xvec] #You don't need to specify Float64[] here - Julia knows what the result will be.
end

```

### `struct` ambiguity

If one of the parameters is a struct containing a member without a concrete type, that can generate sub-optimal code:

```julia-auto
struct MyStruct
   myfield
end

#Will need to do run-time checks to figure out type used to store
#data in "myfield", and call appropriate machine code:
f(s::MyStruct) = 15*s.myfield

x = f(MyStruct(3)) #Needs run-time checks to tell we have an "Int" in "myfield"
x = f(MyStruct(3.0)) #Needs run-time checks to tell we have an "Float64" in "myfield"

```

On the other hand, if the type is parameterized, Julia will compile a different (optimized) version of `f()` for that particular _**(parameterized)**_ struct type:

```julia-auto
struct MyStruct2{T}
   myfield::T
end

#No need for run-time checks here. Julia compiles a different version of f()
#for each type T used in MyStruct2{T}:
f(s::MyStruct2) = 15*s.myfield

#No run-time checks in the following calls!!!:
x = f(MyStruct2(3)) #Julia calls specialized code for f(::MyStruct2{Int})!
x = f(MyStruct2(3.0)) #Julia calls specialized code for f(::MyStruct2{Float64})!

```

---

<div class="post-metadata">

**Author:** ![anon60034542](https://avatars.discourse-cdn.com/v4/letter/a/90ced4/32.png) [@anon60034542](https://discourse.julialang.org/u/anon60034542)\
**Post date:** [May 24, 2021, 3:19pm UTC](https://discourse.julialang.org/t/difference-between-function-that-accepts-abstract-types-and-another-that-uses-type-parameters/61687/4 "2021-05-24T15:19:27Z")

</div>

> On the other hand, if the type is parameterized, Julia will compile a different (optimized) version of `f()`

Does that include structs that have a abstract type annotation, like this:

```julia
struct MyStruct2
   myfield::Number
end

```

Is their no run time check for the above struct with the abstract type of `Number`?

---

<div class="post-metadata">

**Author:** ![heliosdrm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/heliosdrm/32/3851_2.png) [@heliosdrm](https://discourse.julialang.org/u/heliosdrm)\
**Post date:** [May 24, 2021, 4:28pm UTC](https://discourse.julialang.org/t/difference-between-function-that-accepts-abstract-types-and-another-that-uses-type-parameters/61687/5 "2021-05-24T16:28:40Z")

</div>

No, if the annotated type is abstract - and not parametrized - it cannot be optimized. Actually you can think of unannotated fileds as if they were annotated with the abstract type `Any`.
