# Holy traits vs "boolean traits"

**URL:** <https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954>\
**Category:** General Usage\
**Tags:** traits\
**Created:** [March 21, 2024, 9:36pm UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954 "2024-03-21T21:36:15Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![marcpabst](https://avatars.discourse-cdn.com/v4/letter/m/4bbf92/32.png) [@marcpabst](https://discourse.julialang.org/u/marcpabst)\
**Post date:** [March 21, 2024, 9:36pm UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954/1 "2024-03-21T21:36:15Z")

</div>

I’ve been exploring how holy traits are typically implemented in Julia, often seeing patterns like the following:

```julia
SomeTrait(x::Type) = B()
SomeTrait(::Type{<:T}) = A()

somefunc(x::T) where {T} = somefunc(SomeTrait(T), x)
somefunc(::A, x) = :a
somefunc(::B, x) = :b

```

However, I’ve come across a different approach used by libraries such as Tables.jl, where traits are determined by boolean functions, for example:

```julia
Tables.istable(t::T) = true

```

This method also appears to be used by [WhereTraits.jl](https://github.com/jolin-io/WhereTraits.jl). I’m curious if there’s a specific term for these “boolean” traits and whether there’s a general preference or consensus in the Julia community regarding which trait implementation is considered more effective or appropriate.

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [March 21, 2024, 10:00pm UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954/2 "2024-03-21T22:00:55Z")

</div>

In your example, is `traitfunc` supposed to be `somefunc`? Also, the `T` in the definition of `LiquidityStyle` is not defined. And what is the purpose of `LiquidityStyle`? It seems that it is not used later on.

---

<div class="post-metadata">

**Author:** ![marcpabst](https://avatars.discourse-cdn.com/v4/letter/m/4bbf92/32.png) [@marcpabst](https://discourse.julialang.org/u/marcpabst)\
**Post date:** [March 21, 2024, 10:07pm UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954/3 "2024-03-21T22:07:59Z")

</div>

Yes, sorry, it’s late. I just wanted to give an example of what I understand as the conventional implementation for Holy traits, so I copied from [Holy Traits Pattern (book excerpt)](https://ahsmart.com/pub/holy-traits-design-patterns-and-best-practice-book/), which seems to be the accepted authority for that. Thanks, I corrected the original post!

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [March 22, 2024, 12:14am UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954/4 "2024-03-22T00:14:25Z")

</div>

Just do what’s best in the specific sitation.

---

<div class="post-metadata">

**Author:** ![screw\_dog](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/screw_dog/32/48119_2.png) [@screw\_dog](https://discourse.julialang.org/u/screw_dog)\
**Post date:** [March 22, 2024, 1:18am UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954/5 "2024-03-22T01:18:07Z")

</div>

The main difference between “boolean” and Holy traits are that Holy traits operate in the type system whereas boolean traits use values.

The definitions of each are roughly equivalent but Holy traits are more extensible and probably more performant (due to leveraging Julia’s dispatch).

Using boolean (actually symbols, but works the same) traits:

```julia
traitval(::Any) = :none
traitval(::A) = :A
traitval(::B) = :B

somefunc(x::T) where {T} = somefunc(traitval(x), x)
function somefunc(val, x)
  if val == :A
    return :a
  elseif val == :B
    return :b
  else
    # unsure what to do and can't be extended later
  end
end

```

We could use `Val(traitval(...))` and lift this into the type system for dispatch, which is essentially reinventing Holy traits, potentially less efficiently.

Holy traits allow us to easily extend by simply adding something like

```julia
SomeTrait(::Type) = B()
SomeTrait(::Type{<:SomeType) = A()

somefunc(x::T) where {T} = somefunc(SomeTrait(T), x)
somefunc(::A, x) = :a
somefunc(::B, x) = :b

...

struct C end
SomeTrait(::CustomType) = C()
somefunc(::CustomType, x) = :c

```

This means that traits can be added without changing existing code which makes the code more maintainable.

More usefully, since all these operations are operating on types the compiler can generally infer which path will be taken during compilation, avoiding run-time dispatch (ie the `if` statement in the boolean trait example).

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [March 22, 2024, 8:31am UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954/6 "2024-03-22T08:31:39Z")

</div>

See also

> [@Why traits?](https://discourse.julialang.org/t/why-traits/105591):
>
> I’ve been trying to teach my students traits, only to realize that there is something I don’t fully understand. Is there any different between these two approaches to dispatch? struct A end struct B end ## Holy trait trait1(::A) = Val(true) trait1(::B) = Val(false) f(x) = f(x, trait1(x)) f(x, ::Val{true}) = π f(x, ::Val{false}) = "pi" ## Type-inferrable if-else trait2(::A) = true trait2(::B) = false g(x) = trait2(x) ? π : "pi" Both make @code\_warntype happy, and while I know the first one …

---

<div class="post-metadata">

**Author:** ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)\
**Post date:** [March 22, 2024, 9:40am UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954/7 "2024-03-22T09:40:02Z")

</div>

> [@screw\_dog](#):
>
> More usefully, since all these operations are operating on types the compiler can generally infer which path will be taken during compilation, avoiding run-time dispatch (ie the `if` statement in the boolean trait example).

In practice, even with value-based traits the compiler can usually do constant propagation and know which branch to take anyway. So usually there is no run-time penalty.

---

<div class="post-metadata">

**Author:** ![marcpabst](https://avatars.discourse-cdn.com/v4/letter/m/4bbf92/32.png) [@marcpabst](https://discourse.julialang.org/u/marcpabst)\
**Post date:** [March 22, 2024, 1:06pm UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954/8 "2024-03-22T13:06:05Z")

</div>

Yep, that’s what I worried about. In general, I prefer the “boolean traits” as they are closer to what’s usually called traits in other languges (like Rust).

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [March 22, 2024, 1:24pm UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954/9 "2024-03-22T13:24:05Z")

</div>

I think it is better to distinguish

1. traits that return results in the value space (eg values of the same type, such as `true::Bool` or `false::Bool`, an `Enum`, a `Symbol`, etc),

2. traits that encode this information in the type space, using designated types such as `Base.HasShape{N}`, a payload in a `Val{T}`, etc.

Generally, the first option may be more convenient, but can run into problems unless you branch or condition on the trait insider the caller. When you cross a function boundary, the compiler is not guaranteed to propagate the information with constant folding, so the second option is preferable.

Working in the type space is also better if you want to keep things extensible (users can define new traits), and/or use methods instead of branching.

---

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [July 21, 2024, 5:09pm UTC](https://discourse.julialang.org/t/holy-traits-vs-boolean-traits/111954/10 "2024-07-21T17:09:06Z")

</div>

There are quite a few traits packages these days but BinaryTraits.jl allows you to define something that an object can do (positive) or cannot do (negative). Perhaps you can take a look.

> **[GitHub - tk3369/BinaryTraits.jl: Can do or not? It's easy. See...](https://github.com/tk3369/BinaryTraits.jl)**
>
> Can do or not? It's easy. See https://tk3369.github.io/BinaryTraits.jl/dev/ - tk3369/BinaryTraits.jl
