# \[ANN\] TypeParams.jl - Generic type parameters without the fuss

**URL:** <https://discourse.julialang.org/t/ann-typeparams-jl-generic-type-parameters-without-the-fuss/67843>\
**Category:** Package Announcements\
**Tags:** macros, parametric-types, type-stability\
**Created:** [September 8, 2021, 4:12am UTC](https://discourse.julialang.org/t/ann-typeparams-jl-generic-type-parameters-without-the-fuss/67843 "2021-09-08T04:12:36Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![ettersi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ettersi/32/6829_2.png) [@ettersi](https://discourse.julialang.org/u/ettersi)\
**Post date:** [September 8, 2021, 4:12am UTC](https://discourse.julialang.org/t/ann-typeparams-jl-generic-type-parameters-without-the-fuss/67843/1 "2021-09-08T04:12:36Z")

</div>

## Problem

A key feature of Julia is that type annotations are not usually needed to achieve optimal performance.  
For example, `foo(1,2)` runs equally fast regardless whether `foo()` is defined as `foo(a,b) = ...` or `foo(a::Int, b::Float64) = ...`. Unfortunately, there is an important exception to this rule: writing

```julia
struct Foo
    a
    b
end

```

instead of

```julia
struct Foo
    a::Int
    b::Float64
end

```

will discard all compile-time type information on `a` and `b` and hence incur a significant performance penalty. A common workaround to this problem is to introduce a new type parameter for each field:

```julia
struct Foo{A,B}
    a::A
    b::B
end

```

This recovers the flexibility of optional typing and preserves the performance of compile-time types, but keeping the fields and type parameters in sync can be laborious.

## Solution

[`TypeParams`](https://github.com/synchronoustechnologies/TypeParams.jl) eliminates the fuss of generic type parameters by introducing a macro `@typeparams` which allows you to insert such type parameters using a simple syntax:

```julia
@typeparams struct Foo
    a::{}
    b::{}
end

```

It further supports expressing type constraints with zero syntax overhead:

```julia
@typeparams struct Foo
    a::{<:Integer}
    b::{<:Real}
end

```

Finally, `@typeparams` plays well with other features of the Julia language:

- Explicit type parameters:

- The `@kwdef` macro:

## Acknowledgements

This package is heavily inspired by [AutoParameters.jl](https://github.com/pengwyn/AutoParameters.jl).

---

<div class="post-metadata">

**Author:** ![oschulz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oschulz/32/2998_2.png) [@oschulz](https://discourse.julialang.org/u/oschulz)\
**Post date:** [September 8, 2021, 7:20am UTC](https://discourse.julialang.org/t/ann-typeparams-jl-generic-type-parameters-without-the-fuss/67843/2 "2021-09-08T07:20:14Z")

</div>

Is TypeParams compatible with `Parameters.@with_kw` (goes a bit further than `Base.@kwdef`) as well?

---

<div class="post-metadata">

**Author:** ![genkuroki](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/genkuroki/32/18030_2.png) [@genkuroki](https://discourse.julialang.org/u/genkuroki)\
**Post date:** [September 8, 2021, 9:09am UTC](https://discourse.julialang.org/t/ann-typeparams-jl-generic-type-parameters-without-the-fuss/67843/3 "2021-09-08T09:09:52Z")

</div>

This package is very similar to [ConcreteStructs.jl](https://github.com/jonniedie/ConcreteStructs.jl), which I often use.

Recently, I tried to make ConcreteStructs.jl usable in combination with Parameters.jl. My personal conclusion is that it is necessary to change `Parameters.@with_kw` expanding the macros in the argument expression, [like `Base.@kwdef`](https://github.com/JuliaLang/julia/blob/4931faa34a8a1c98b39fb52ed4eb277729120128/base/util.jl#L455).

See

- [https://github.com/genkuroki/public/blob/main/0019/ConcreteStructs.jl%20with%20Parameters.jl.ipynb](https://github.com/genkuroki/public/blob/main/0019/ConcreteStructs.jl%20with%20Parameters.jl.ipynb)
- [https://github.com/jonniedie/ConcreteStructs.jl/issues/4#issuecomment-913998030](https://github.com/jonniedie/ConcreteStructs.jl/issues/4#issuecomment-913998030)

Then TypeParams.jl also becomes compatible with Parameters.jl.

Example:

```julia
using TypeParams
using Parameters

@eval Parameters macro with_kw(typedef)
    typedef = macroexpand( __module__ , typedef) # inserted
    return esc(with_kw(typedef, __module__ , true))
end

@with_kw @typeparams struct Foo_with_kw
    a::{} = 1
    b::{} = 2.0
    c::{<:AbstractString} = "three"
end

Foo_with_kw()

```

Output:

```julia
Foo_with_kw{Int64, Float64, String}
  a: Int64 1
  b: Float64 2.0
  c: String "three"

```

Jupyter notebook: [https://github.com/genkuroki/public/blob/main/0019/On%20TypeParams.jl.ipynb](https://github.com/genkuroki/public/blob/main/0019/On%20TypeParams.jl.ipynb)

---

<div class="post-metadata">

**Author:** ![ettersi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ettersi/32/6829_2.png) [@ettersi](https://discourse.julialang.org/u/ettersi)\
**Post date:** [September 8, 2021, 10:19am UTC](https://discourse.julialang.org/t/ann-typeparams-jl-generic-type-parameters-without-the-fuss/67843/4 "2021-09-08T10:19:28Z")

</div>

No, `@typeparams` is currently not compatible with `@with_kw`, as @genkuroki has already pointed out. @genkuroki’s fix of making `@with_kw` expand its argument first works in simple cases, but I expect it would break the `@assert` and `@deftype` features of `@with_kw`.

Maybe `@with_kw @typeparams` could be made to work by deleting the [`macroexpand` in `@typeparams`](https://github.com/synchronoustechnologies/TypeParams.jl/blob/11a31a2b582911df28f427d32f7b985be08cac3b/src/TypeParams.jl#L38), but then `@typeparams` may also require quite a bit of extra work to make it handle the potentially vastly more complicated input.

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [September 8, 2021, 11:52am UTC](https://discourse.julialang.org/t/ann-typeparams-jl-generic-type-parameters-without-the-fuss/67843/5 "2021-09-08T11:52:07Z")

</div>

Personally, I would love to see such improvement in native Julia without explicit usage of macros from packages. It seems quite natural addition for performance and is rare to see a situation where `Any` type is preferred.

---

<div class="post-metadata">

**Author:** ![oschulz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oschulz/32/2998_2.png) [@oschulz](https://discourse.julialang.org/u/oschulz)\
**Post date:** [September 17, 2021, 5:19pm UTC](https://discourse.julialang.org/t/ann-typeparams-jl-generic-type-parameters-without-the-fuss/67843/6 "2021-09-17T17:19:58Z")

</div>

> [@juliohm](#):
>
> Personally, I would love to see such improvement in native Julia

It would indeed really be nice if we could just write

```julia
struct MyStruct{T<:Integer}
    a::T = 1
    b<:Real = 2.0
    c<:AbstractArray{<:Real} = [3, 4, 5]
end

```

resulting in additional “anonymous” type parameters. There is, of course, some abuse potential - instead of using the same type parameter for several fields (where appropriate), it would be easier for users to use the syntax above, resulting in types with (potentially) a large number of type parameters.

---

<div class="post-metadata">

**Author:** ![jonniedie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonniedie/32/12842_2.png) [@jonniedie](https://discourse.julialang.org/u/jonniedie)\
**Post date:** [October 20, 2021, 12:12am UTC](https://discourse.julialang.org/t/ann-typeparams-jl-generic-type-parameters-without-the-fuss/67843/7 "2021-10-20T00:12:39Z")

</div>

> [@oschulz](#):
>
> It would indeed really be nice if we could just write
> 
> ```julia
> struct MyStruct{T<:Integer}
> a::T = 1
> b<:Real = 2.0
> c<:AbstractArray{<:Real} = [3, 4, 5]
> end
> 
> ```

If you use ConcreteStructs, you can throw a `Base.@kwdef @concrete` in front of that struct definition and it should work as expected.
