# \[ANN\] ArgumentModes.jl - an implementation of set-of-symbols like type for function arguments

**URL:** <https://discourse.julialang.org/t/ann-argumentmodes-jl-an-implementation-of-set-of-symbols-like-type-for-function-arguments/71429>\
**Category:** Package Announcements\
**Tags:** argument, dispatch, val\
**Created:** [November 13, 2021, 1:10pm UTC](https://discourse.julialang.org/t/ann-argumentmodes-jl-an-implementation-of-set-of-symbols-like-type-for-function-arguments/71429 "2021-11-13T13:10:36Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Icarus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/icarus/32/30777_2.png) [@Icarus](https://discourse.julialang.org/u/Icarus)\
**Post date:** [November 13, 2021, 1:10pm UTC](https://discourse.julialang.org/t/ann-argumentmodes-jl-an-implementation-of-set-of-symbols-like-type-for-function-arguments/71429/1 "2021-11-13T13:10:36Z")

</div>

I’d like to announce the package [ArgumentModes.jl](https://github.com/kareevia/ArgumentModes.jl) (also available in the registry) which provides type `Mode` for usage as a function argument type and in function calls. Conceptually it is similar to `Val` type, but working only with symbols. The main advantage of `Mode` is that it could contain a set of symbols, and the dispatch process can refer to exact combination of the symbols when the method is being chosen. Moreover, `Mode` type of a function argument could be defined to be able to accept only prescribed list of symbols – then the method would be chosen only if a `Mode` instance has all or any subset of the prescribed symbols. As an optional feature – each of symbols might also have additional parameters with prescribed types, and the dispatch process considers the types of the parameters’ types.

As I see, the main advantages of using `Mode` could be:

- Specialization and dispatch based on the value of a `Mode` instance - in many case no additional code or runtime overhead is needed.
- Since the list of allowed symbols for a method is defined directly in a function signature, in most cases the dispatch process would control for typos in symbols names. Moreover, the “Closest candidates are:” section of `MethodError` would contain meaningful description of possible symbols for corresponding methods.

Here an example:

```julia-repl
julia> f(x::Real) = println("Number: $x")
       f(x::Mode[:iterator => Any]) = println("Values from iterator: $(x[]...)")
       f(x::Mode[:fromargs], y...) = println("Fromargs: $(y...)")
       function f(x::Union{Int, Mode[:a, :b, :c => Int]})
           checkmode(x, :a) do _; println(":a") end
           checkmode(x, :c) do c; println(":c = $c") end
           checkmode(x, :b) && println(":b")
           if x isa Int; println("x = $x") end
       end
f (generic function with 4 methods)

julia> methods(f)
# 4 methods for generic function "f":
[1] f(x::Mode[:iterator => Any]) in Main at REPL[34]:1
[2] f(x::Mode[:fromargs], y...) in Main at REPL[36]:1
[3] f(x::Union{Int64, Mode[:c => Int64, :a, :b]}) in Main at REPL[38]:1
[4] f(x::Real) in Main at REPL[33]:1

julia> f(25.0)
Number: 25.0

julia> f(Mode(:iterator)=> 1:5)
Values from iterator: 12345

julia> f(Mode(:fromargs), 1, 2, 3, 4, 5)
Fromargs: 12345

julia> f(1)
x = 1

julia> f(Mode(:a, :c => 125))
:a
:c = 125

julia> f(Mode(:fromargs, :iterator => 1:5), 2)
ERROR: MethodError: no method matching f(::Mode[==, :iterator => UnitRange, :fromargs], ::Int64)
Closest candidates are:
  f(::Mode[:fromargs], ::Any...) at REPL[36]:1
Stacktrace:
 [1] top-level scope
   @ REPL[59]:1

```

The specialization of `Mode` with the list of accepted symbols for function arguments is generated using the call of form `Mode[:symbol1, :symbol2, symbol3 => Tuple{Int, AbstractString}, ...]`. An instance of `Mode` for a function call is created with constructor `Mode(:symbol1, symbol2, :symbol3 => (1, "Hello"), ... )`. Another form for the instance construction (which is a product of my dislike of the nested parentheses) is `Mode(:symbol1) ~ Mode(:symbol2) ~ Mode(:symbol3)=>(1, "Hello") ~ ...`.

The value of an additional parameter for a symbol could be extracted from a `Mode` instance as `m[:symbol1]`. Or for several symbols (as a tuple): `m[:symbol1, :symbol2, ...]`. A call `m[]` would test whether the instance `m` has exactly one symbol and return the value associated with it.

A function `checkmode` is also defined to test a `Mode` instance for the symbols. For example:

```julia-repl
julia> m = Mode(:a => 1, :b => 2.5, :c)
Mode[==, :b => Float64, :a => Int64, :c]((a = 1, b = 2.5, c = nothing))

julia> checkmode(m, :a)
true

julia> checkmode(m, :d)
false

julia> checkmode(m, |, :a, :d)
true

julia> checkmode(m, &, :a, :d)
false

julia> checkmode(m, ==, :a, :b)
false

julia> checkmode(m, ==, :a, :b, :c)
true

julia> checkmode(m, :a) do a; @show a end
a = 1
1

julia> checkmode(m, :a, :b) do a, b; @show a, b end
(a, b) = (1, 2.5)
(1, 2.5)

julia> checkmode(m, :a, :e) do x, y; @show x, y end

julia> checkmode(12, :a)
false

```

Please, refer to the in-julia documentation of the type and the function for more detailed information.

Series of tests showed that the current implementation of `Mode` fully compiles out when it used for function arguments and method dispatch, so it seems that there is no runtime overhead for using it (at least for the use cases considered in the tests).

I’m not sure if the concept is actually any good, I guess I would know myself only from attempts of using it. As of now I see 2 uses of `Mode`:

1. Replacement of using ordinary `Symbol` as a function argument as a flag  
parameter. Here `Mode` allows to explicitly declare a function argument as a  
set of symbols (flags) with a distinct list of accepted symbols for each of  
a function’s methods. For example, `open(f, Mode(:read))`, `open(f, Mode(:write))`,  
`open(f, Mode(:write, :sync))` might correspond (depending on the design)  
to 3 different methods.

2. The way to explicitly show in which meaning a value to a function argument  
is provided. This might be useful when it is not possible to distinct the  
meaning of an argument only by its type.
