# How can interfaces work with symbolic-diff?

**URL:** <https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565>\
**Category:** General Usage\
**Tags:** interface\
**Created:** [June 2, 2025, 4:46pm UTC](https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565 "2025-06-02T16:46:07Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [June 2, 2025, 4:46pm UTC](https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565/1 "2025-06-02T16:46:07Z")

</div>

In @MilesCranmer’s [post about abstract typing](https://discourse.julialang.org/t/efficiency-of-abstract-types-vs-any/129518), @ChrisRackauckas [wrote](https://discourse.julialang.org/t/efficiency-of-abstract-types-vs-any/129518/2)

> [@ChrisRackauckas](#):
>
> Being able to state that for every dispatch of `f`, you must have `f(a::T,b::T)::T` would be able to guarantee such relations, and you would then be able to short circuit many difficult inference problems.

I didn’t want to take that thread off topic, so pulling this out. I’m interested to see @ChrisRackauckas mention function interfaces as a solution world splitting because my understanding from [this post](https://discourse.julialang.org/t/specifying-function-contract/108983/13) was that interfaces would mess up tracing for symbolic-diff (where `Base.:(==)(::T, ::T)::T` instead of returning `Bool`).

How can they be reconciled?

Edit: I understand how soft requirements like the various interface packages would work, but if it were a language feature to allow the compiler to be certain about a return value, then that seems like that strictness would be an issue.

Edit2: I also understand that there is a difference between `isequal` and `==`, but I assume that there are functions that might have an interface defined that would preclude the normal tracing mechanisms.

Edit3: change references to auto diff to symbolic diff. Thanks @gdalle !

---

<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:** [June 2, 2025, 6:49pm UTC](https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565/2 "2025-06-02T18:49:25Z")

</div>

> [@mrufsvold](#):
>
> interfaces would mess up tracing for auto-diff (where `Base.:(==)(::T, ::T)::T` instead of returning `Bool`).

Just to clarify here: having `==` return a non-boolean is only necessary for symbolic differentiation (and other forms of tracing like sparsity detection), because the “numbers” involved there do not carry actual values around.  
By contrast, automatic (or algorithmic) differentiation usually works by following the branch of code taken based on the input values, so `==` does yield a boolean. There might be other issues with strict interfaces but at least I think this one doesn’t apply for packages like ForwardDiff.jl or Mooncake.jl.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [June 2, 2025, 7:49pm UTC](https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565/3 "2025-06-02T19:49:39Z")

</div>

That would give issues with tracing though, so SparseConnectivityTracer and Reactant would be two other cases that likely don’t treat that always as bool

```julia
julia> using SparseConnectivityTracer

julia> detector = TracerSparsityDetector()
TracerSparsityDetector()

julia> f(x) = [x[1]^2, @show(x[1] == x[2]) * 2 * x[1] * x[2]^2, sin(x[3])]
f (generic function with 1 method)

julia> x = rand(3)
3-element Vector{Float64}:
 0.1932411340661383
 0.4884474701278184
 0.09024014091785981

julia> jacobian_sparsity(f, x, detector)
x[1] == x[2] = SparseConnectivityTracer.GradientTracer{SparseConnectivityTracer.IndexSetGradientPattern{Int64, BitSet}}(SparseConnectivityTracer.IndexSetGradientPattern{Int64, BitSet}(BitSet([])), false)
3×3 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:
 1 ⋅ ⋅
 1 1 ⋅
 ⋅ ⋅ 1

```

But they clearly aren’t the only ones. There’s many others that you can think of too.

---

<div class="post-metadata">

**Author:** ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)\
**Post date:** [June 2, 2025, 11:04pm UTC](https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565/4 "2025-06-02T23:04:15Z")

</div>

So is there a world where a Julia 2.0 could have enforced interfaces and tracing or do we always need interfaces to be warnings/documentation? Maybe you could use a Cassette-like approach to turn off interface checking for a call graph?

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [June 3, 2025, 12:39am UTC](https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565/5 "2025-06-03T00:39:28Z")

</div>

> [@mrufsvold](#):
>
> So is there a world where a Julia 2.0 could have enforced interfaces and tracing or do we always need interfaces to be warnings/documentation?

Well this is only an issue if `Base.:(==)(::T, ::T)::Bool` is the interface. Though, that can’t be true since `Base.:(==)(::Missing, ::Missing)::Missing` already breaks that interface… so this is really just a nice example that interfaces can be more difficult than you think. And that’s okay: all of these packages, even statistics packages with missing, would be fine if we accept `Base.:(==)(::T, ::T)::T` as the interface.

> [@mrufsvold](#):
>
> Maybe you could use a Cassette-like approach to turn off interface checking for a call graph?

I would like a better interface for going into tracing mode. Currently the only nice interfaces to activate tracing require using (/abusing) dispatch, which effectively means you have to do `@trace f(x,y)` in front of any function you want to trace (where you then apply your tracer types in the macro and do the call).

If activating a abstract interpretation can be given a simpler interface then Symbolics, SCT, etc. could probably adopt that.

---

<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:** [June 3, 2025, 4:44am UTC](https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565/6 "2025-06-03T04:44:43Z")

</div>

Maybe CassetteOverlay.jl could be a way forward, but it looks a bit dead?

> <https://github.com/JuliaDebug/CassetteOverlay.jl/issues/62>
>
> The repository description mentions that this project is "experimental".
> What ar…e the caveats/limitations that should be considered before using CasetteOverlay?

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [June 3, 2025, 8:31am UTC](https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565/7 "2025-06-03T08:31:40Z")

</div>

IMO this is the entire benefit of interfaces. They have to be opt-in. Then your symbolic type with a non-bool `==` would simply _ **not** _ opt-in. And every function that requires the interface would reject your type. Which is precisely what we want!

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [June 3, 2025, 8:38am UTC](https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565/8 "2025-06-03T08:38:32Z")

</div>

I think Rust’s approach to interfaces is really well designed:

```rust
fn dump_numbers(out: &mut impl Write) -> io::Result<()> {
    for n in 1..=10 {
        writeln!(out, "{n}")?;
    }
    out.flush()
}

```

The “`out: &mut impl Write`” part says:

- The function takes an argument `out`.
- It modifies that variable (`&mut`), so you have to pass a mutable reference. The compiler checks this for you.
- The variable needs to be of a type that implements (`impl`) the `Write` interface. The compiler also checks this.

Thus, only types that declare all necessary methods for the `Write` interface are allowed to be passed to that function.

I think Julia could do something like this and make it even cooler, because you could have multiple dispatch based on different levels of adherence to an interface 🤩

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [June 3, 2025, 9:01am UTC](https://discourse.julialang.org/t/how-can-interfaces-work-with-symbolic-diff/129565/9 "2025-06-03T09:01:18Z")

</div>

100% agree. I think there’s just a lot of very narrow thinking that tends to go on with interfaces. I want all arrays to be mutable, so there for AbstractArray requires mutability. I want all `==` to emit `Bool`, so all `==` must emit `Bool`. But indeed `BasicLogical` or something meaning “I assert that these set of logical rules need to hold in this function” and then erroring on types for which it doesn’t, that is what would make them very useful and expansive, rather than just discarding everything someone feels is non-standard.

> [@gdalle](#):
>
> Maybe [CassetteOverlay.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/CassetteOverlay) could be a way forward, but it looks a bit dead?
> 
> [Document caveats · Issue #62 · JuliaDebug/CassetteOverlay.jl · GitHub](https://github.com/JuliaDebug/CassetteOverlay.jl/issues/62)

It’s maintained, though most of that stack is severely underdocumented.
