When to use `DomainError` instead of `ArgumentError`?

For example, if an algorithm can only be used on square matrices, which error should I throw?

function square_matrix_algorithm(A::AbstractMatrix)
    if size(A, 1) != size(A, 2)
        throw(ArgumentError("This function can only be used on square matrices!"))
        # Or
        throw(DomainError(A, "This function can only be used on square matrices!"))
    end
    ...
end
1 Like

ArgumentError is documented vaguely because it’s used for quite a few things, like the wrong number or type of arguments. Arity and type can also be handled with method dispatch and absent methods (MethodError), but sometimes things are written with branches in one method.

DomainError more specifically deals with a subset of a Julia type’s instances being invalid in a particular context, like negative reals for sqrt. In isolation, it does technically apply here, but there is an even more specific DimensionMismatch for square matrices.

julia> using LinearAlgebra

julia> det([0 1
            2 3]) # determinant defined for square matrices
-2.0

julia> det([0 1 10
            2 3 20])
ERROR: DimensionMismatch: matrix is not square: dimensions are (2, 3)

As a general rule, the more specific error is proper, but in practice, check similar functions in Base, the standard library, or popular third-party packages, in that order.

1 Like

Thanks! That’s another error type that I have been wondering how to use for long. Its docs is very vague, too:

The objects called do not have matching dimensionality.

I always use it to compare 2 or more arrays’ sizes:

if size(A) != size(B)
    throw(DimensionMismatch("..."))
end

But I always want to know: if I require a matrix of size 3x3 as argument, should I use DimensionMismatch or ArgumentError?

I would say that DimensionMismatch is the correct type of error in this case

2 Likes

^An internal LinearAlgebra example in v1.10.5

function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix,
                    _add::MulAddMul = MulAddMul())
    require_one_based_indexing(C, A, B)
    if !(size(A) == size(B) == size(C) == (3,3))
        throw(DimensionMismatch(lazy"A has size $(size(A)), B has size $(size(B)), C has size $(size(C))"))
    end
    ...

Thanks! But I think this DimensionMismatch comes from size(A) == size(B) == size(C).

Also, how about this? Should I use DomainError?

if radius < zero(radius)
    throw(DomainError(radius, "radius must be non-negative!"))
end

The == (3,3) right after it in the same line is also a critical trigger of the DimensionMisMatch, and the function name includes 3x3 to indicate its intent. Note that det only takes 1 matrix, so “mismatch” doesn’t inherently compare multiple matrices, it refers to any unsuitable dimensions.

That seems right to me, and Base has the same check.

1 Like