# Julia: function composition: how to correctly split arguments?

**URL:** https://discourse.julialang.org/t/julia-function-composition-how-to-correctly-split-arguments/61433
**Category:** New to Julia
**Tags:** composition
**Created:** [May 19, 2021, 11:34am UTC](https://discourse.julialang.org/t/julia-function-composition-how-to-correctly-split-arguments/61433 "2021-05-19T11:34:02Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![ptoche](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ptoche/32/23554_2.png) [@ptoche](https://discourse.julialang.org/u/ptoche)
#### Post date: [May 19, 2021, 11:34am UTC](https://discourse.julialang.org/t/julia-function-composition-how-to-correctly-split-arguments/61433/1 "2021-05-19T11:34:02Z")

</div>

How to define functions/functors and split arguments?

I have a struct of fixed parameters, `P`, a mutable struct of variables, `V`, and three constructors for methods `F`, `G`, and `H`, where `H` is the composition of `F` and `G`. The constructor for `F` has three parts: (1) The struct that holds parameters `F`; (2) a function `f(q,x)` that unpacks its arguments and returns some calculation; (3) a functor-type method `F(q)(x)` defined on struct `F`. The argument tuple `(q,x)` is split between a typed `q` and some vector `x`. The construction of `G` and `H` is similar.

The main purpose of this design is to be able to change the functional forms and the parameters while keeping the overall architecture. I’ve managed to make this work in simple cases, but am having difficulty when `P` and `V` hold more than one parameter. So let me review what appears to perform as intended and what doesn’t.

Simple case: `F` is typed with a single fixed parameter `a` and an instance can be created with `F(a0)(x0)`, for some concrete `a0` and `x0`. Works.

Mixed types: `G` is typed with both a fixed parameter and a variable, `b` and `β`, and an instance can be created with `G(b0, β0)(x0)`, for some concrete `b0`, `β0` and `x0`.

Composition: `H` is typed with both `P` and `V` and the method must properly unpack and access the fields. That is, the argument tuple `q` holds an instance `p` of `P` and `v`of `V`, like so `q = p, v`. I can get a composition function `h(q,x)` to work, but I can’t get a method defined directly on the type `H` to work (i.e. `H(q)(x)` does not work). I have tried several variations of `H(q,x)` without success. Trying to pick up a hint from the error message, I tried to type with `::Tuple{P,V}`. I also tried to define `H(q)(x)` as an inner method, but that didn’t work at all.

Thanks for your suggestions!

```
"""
`P`
struct to hold all fixed parameters
"""
struct P
    a :: Float64 # not used in this example
    b :: Vector{Float64}
end
function P(; a = 1.0, b = [2.0, 3.0])
    P(a, b)
end 

"""
`V`
mutable struct to hold all variable parameters
"""
mutable struct V
    α :: Float64
    β :: Vector{Float64}
end
function V(; α = 0.0, β = [0.0, 0.0])
    V(α, β)
end 

"""
`F(x)`
functor method defined on struct holding fixed parameter a
"""
struct F
    a :: Float64
end
(dummy::F)(x) = f(dummy.a, x) # one-line functor definition
function f(a, x)
    return x.^(a) # note the broadcasting dot
end

"""
`G(b, β, x)`
   b :: Vector{Float64}
   β :: Vector{Float64}
"""
struct G
    b :: Vector{Float64}
    β :: Vector{Float64}
end
function (q::G)(x)
    return g(q, x)
end
function g(q, x)
    b = q.b; β = q.β
    return sqrt.(b .* β) * x
end

"""
`H(q, x)`
functor method defined as the composition of F and G defined on a struct typed with `p::P` (fixed parameters) and `v::V` (variables)
"""
struct H{FG}
    h :: FG
end
function (q::H)(x)
    return h(q, x)
end 
function h(q, x)
    p, v = q # split pars/vars
    a, b, β = p.a, p.b, v.β # unpack
    return (F(a) ∘ G(b,β))(x)
end 

```

A few checks to see that `F` and `G` work as expected, while `H` does not (but `h` does):

```
# Check F:
((a,x) -> x.^(a))(2.0,2.0)
## 4.0

F(2.0)(2.0)
## 4.0 

# Check G:
((b,β,x) -> sqrt.(b.*β)*x)([2.0, 3.0], [4.0, 5.0], 2.0)
## 2-element Vector{Float64}:
## 5.656854249492381
## 7.745966692414834

G([2.0, 3.0], [4.0, 5.0])(2.0)
## 2-element Vector{Float64}:
## 5.656854249492381
## 7.745966692414834

# Create instances of P() and V()
p0 = P(a = 2.0)
## P(1.0, [2.0, 3.0])

v0 = V(β = [4.0, 5.0])
## V(0.0, [4.0, 5.0])

# Check H:
(F(2.0) ∘ G([2.0, 3.0], [4.0, 5.0]))(2.0)
## 2-element Vector{Float64}:
## 32.00000000000001
## 60.00000000000001

(F(p0.a) ∘ G(p0.b, v0.β))(2.0)
## 2-element Vector{Float64}:
## 32.00000000000001
## 60.00000000000001

h((p0, v0), 2.0)
## 2-element Vector{Float64}:
## 32.00000000000001
## 60.00000000000001

H((p0,v0))(2.0)
## ERROR: LoadError: MethodError: no method matching iterate(::H{Tuple{P, V}})

H(p0,v0)(2.0)
## ERROR: LoadError: MethodError: no method matching H(::P, ::V)

```

---

<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 19, 2021, 1:05pm UTC](https://discourse.julialang.org/t/julia-function-composition-how-to-correctly-split-arguments/61433/2 "2021-05-19T13:05:52Z")

</div>

Instead of:

> [@ptoche](#):
>
> ```julia
> function (q::H)(x)
> return h(q, x)
> end 
> 
> ```

It should be:

```julia
function (q::H)(x)
    return h(q.h, x)
end 

```

---

<div class="post-metadata">

### Author: ![ptoche](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ptoche/32/23554_2.png) [@ptoche](https://discourse.julialang.org/u/ptoche)
#### Post date: [May 19, 2021, 7:13pm UTC](https://discourse.julialang.org/t/julia-function-composition-how-to-correctly-split-arguments/61433/3 "2021-05-19T19:13:04Z")

</div>

Sweet! Thanks heliosdrm!

and you call it with:

```
H((p0,v0))(2.0)

```

P.S. `q.h` that’s voodoo?

---

<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: [May 19, 2021, 7:29pm UTC](https://discourse.julialang.org/t/julia-function-composition-how-to-correctly-split-arguments/61433/4 "2021-05-19T19:29:22Z")

</div>

> [@ptoche](#):
>
> ```julia
> struct H{FG}
> h :: FG
> end
> function (q::H)(x)
> return h(q, x)
> end 
> 
> ```

the argument `q` of your function `(q::H)` is of type `H`, and your type `H` has a field `h`, thus the `q.h`.

---

<div class="post-metadata">

### Author: ![ptoche](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ptoche/32/23554_2.png) [@ptoche](https://discourse.julialang.org/u/ptoche)
#### Post date: [May 19, 2021, 8:00pm UTC](https://discourse.julialang.org/t/julia-function-composition-how-to-correctly-split-arguments/61433/5 "2021-05-19T20:00:15Z")

</div>

> [@lmiq](#):
>
> the argument `q` of your function `(q::H)` is of type `H` , and your type `H` has a field `h` , thus the `q.h` .

Makes perfect sense, thanks Leandro!
