# \[ANN\] Announcing ForwardMethods.jl

**URL:** <https://discourse.julialang.org/t/ann-announcing-forwardmethods-jl/100902>\
**Category:** Package Announcements\
**Tags:** package, announcement, macros\
**Created:** [June 27, 2023, 4:22pm UTC](https://discourse.julialang.org/t/ann-announcing-forwardmethods-jl/100902 "2023-06-27T16:22:45Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![curtd](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/curtd/32/15868_2.png) [@curtd](https://discourse.julialang.org/u/curtd)\
**Post date:** [June 27, 2023, 4:22pm UTC](https://discourse.julialang.org/t/ann-announcing-forwardmethods-jl/100902/1 "2023-06-27T16:22:45Z")

</div>

I’m happy to announce [ForwardMethods.jl](https://github.com/curtd/ForwardMethods.jl) v1.1, a package made to help remove some boilerplate when defining forwarded/delegated methods.

## Method Definition Forwarding

Concretely, suppose you have a composite type

```julia
struct A
  d::Dict{String,Any}
end

```

If you’re looking to delegate or forward `Dict` methods from an object `x::A` to `x.d`, a standard approach in Julia would be to write

```julia
for f in (:length, :isempty, :empty!)
   @eval Base.$f(x::A) = $f(getfield(x, :d))
end

```

which works fine if all of the functions you’re interested in forwarding have a) the same number of arguments and b) the position of `x` in the argument list remains constant. When forwarding a larger number of methods with a greater heterogeneity in their argument list, it can be annoying to keep track of a large number of method signatures, especially if you’d like much more control over the type signatures involved in the forwarded methods.

The `@forward_methods` macro allows you to avoid some of these issues by writing

```julia
@forward_methods A field=d Base.length(x::A) Base.isempty(x::A) Base.getindex(x::A, k) Base.setindex!(x::A, v, i)

```

or, even more compactly,

```julia
@forward_methods A field=d Base.length Base.isempty Base.getindex(_, k) Base.setindex!(x::A, v, i)

```

Here `0`-argument expressions are implicitly expanded to a `1`-argument function and `_` is shorthand for `x::A`.

The `field` keyword argument supports a number of different parameters including nested dotted expressions, which forward to deeply nested structures, e.g.,

```julia
@forward_methods A field=b.c.d Base.length

```

will forward `Base.length(x::A)` to `Base.length(getfield(getfield(getfield(A, :b), :c), :d))`.

`@forward_methods` will also handle argument signatures with `Type` arguments, e.g.,

```julia
@forward_methods A field=b.c.d Base.eltype(::Type{A})

```

will define

```julia
Base.eltype(::Type{A}) = Base.eltype(fieldtype(fieldtype(fieldtype(A, :b), :c), :d))

```

## Interface Forwarding

Similarly, the `@forward_interfaces` macro allows you to forward entire interfaces (i.e., named collections of particular method signatures) rather than have to write out/remember each method signature by hand.

You can also post-apply an expression to the resulting forwarded method via the `map` keyword argument, which allows you to do some nice fancy things like

```julia
 struct LockableDict{K,V} <: AbstractDict{K, V}
        d::Dict{K,V}
        lock::ReentrantLock
 end
 @forward_interface LockableDict{K,V} field=lock interface=lockable
 @forward_interface LockableDict{K,V} field=d interface=dict map=begin lock(_obj); try _ finally unlock(_obj) end end
 d = LockableDict(Dict{String,Int}(), ReentrantLock())

```

And now all of your standard dictionary calls like e.g., `d["a"] = 0` will be thread-safe! (And horribly non-performant, but that’s not the point here).

This package is basically just a fancy copy+paste system but takes into account the syntax of Julia method signatures. Hopefully it makes using the Composite design pattern easier to implement for your types.

Happy forwarding!

---

<div class="post-metadata">

**Author:** ![curtd](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/curtd/32/15868_2.png) [@curtd](https://discourse.julialang.org/u/curtd)\
**Post date:** [July 4, 2023, 4:19pm UTC](https://discourse.julialang.org/t/ann-announcing-forwardmethods-jl/100902/2 "2023-07-04T16:19:05Z")

</div>

# Version 1.3.1

Version 1.3.1 of [ForwardMethods.jl](https://github.com/curtd/ForwardMethods.jl) is out now!

### `@define_interface`

I’ve added a new macro for defining generic (i.e., apply to any `Struct` type) interfaces that don’t necessarily correspond to forwarding methods to a single field in the struct. The currently supported interfaces are

- the `getfields` and `setfields` interfaces: Given `x::T`, these interfaces define `$field(x) = getfield(x, $field)` and `$field!(x, v) = setfield!(x, $field, v)` for each `field in fieldnames(T)`, respectively. This allows you to write, for instance,

```julia
mutable struct A 
  key1::String
  key2::Int
end

@define_interface A interface=(getfields, setfields)
a = A("a", 0)
key1(a)
key1!(a, "b")
key1(a)

key2(a)
key2(a, 1)
key2(a)

```

Which can used to present a more easily extendable interface to your types compared to the built-in `getproperty`/`setproperty!` methods.

- `properties` interface: Given `x::T` with struct-type subfields `y1::S1`, …, `yn::SN`, this interface defines `Base.getproperty(x, k)` and `Base.setproperty!(x, k, v)` such that when `k in fieldnames(Sk)`, the corresponding method is forwarded to `yk`. This allows you to treat types composed of sub-types in a uniform way, e.g.,

```julia
julia> struct A
           key1::Int
           key2::Bool
       end

julia> struct B
           key3::String
           key4::Float64
       end

julia> struct C
           a::A
           b::B
       end

julia> @define_interface C interface=properties delegated_fields=(a,b)

julia> c = C(A(1, true), B("a", 0.0))
C(A(1, true), B("a", 0.0))

julia> (key1=c.key1, key2=c.key2, key3=c.key3, key4=c.key4, a=c.a, b=c.b)
(key1 = 1, key2 = true, key3 = "a", key4 = 0.0, a = A(1, true), b = B("a", 0.0))

```

- `equality` interface: Given `x::T`, defines `Base.:(==)` (or `Base.isequal`) in the obvious way, i.e., as

```julia
Base.:(==)(x::T, y::T) = all( getfield(x,k) == getfield(y,k) for k in fieldnames(T))

```
