How to disable @assert in Julia

Viewed 1084

As an old C programmer, I use lots of asserts in my code. Now I want to globally switch them off to speed things up. What is the best practice way to do that?

3 Answers

There is no built-in option / command line flag to disable @asserts globally, yet(!).

For now, you can define a @myassert macro which, depending on a global switch, is a no-op or a regular @assert:

asserting() = false # when set to true, this will enable all `@myassert`s

macro mayassert(test)
  esc(:(if $(@__MODULE__).asserting()
    @assert($test)
   end))
end

f(x) = @mayassert x < 2 

(taken from https://discourse.julialang.org/t/assert-alternatives/24775/14)

Although it would be nice to have this feature, the need for @asserts in your code can be reduced by defining and dispatching on your own types. For example, suppose you have a function foo(t::TimeType) = t, but you only want to accept times that are a multiple of five minutes. You can create a new type with this requirement:

using Dates

struct FiveMinuteMultiple
    t::DateTime

    function FiveMinuteMultiple(y, m, d, h, mi)
        if mi%5 != 0
            throw(DomainError(m, "the minute argument must be a multiple of 5"))
        end
        new(DateTime(y, m, d, h, mi))
    end
end

Now you literally can't create a FiveMinuteMultiple that is not a multiple of five minutes:

julia> t = FiveMinuteMultiple(2016, 7, 15, 4, 23)
ERROR: DomainError with 7:
the minute argument must be a multiple of 5
Stacktrace:
 [1] FiveMinuteMultiple(::Int64, ::Int64, ::Int64, ::Int64, ::Int64) at ./REPL[2]:5
 [2] top-level scope at none:0

julia> t = FiveMinuteMultiple(2016, 7, 15, 4, 25)
FiveMinuteMultiple(2016-07-15T04:25:00)

So if you now define foo(t::FiveMinuteMultiple) = t, you no longer need an @assert to verify that the argument is a time that is a multiple of five minutes. Sure, you still have to pay the cost of argument checking when you construct the FiveMinuteMultiple, but unless it's a hot inner loop you probably want that additional data validation anyways.

Advantages:

  • Method dispatch guarantees that the arguments to your functions are of the correct type.
  • You can avoid duplicating the same assertion across multiple functions foo(t::FiveMinuteMultiple), bar(t::FiveMinuteMultiple), and baz(t::FiveMinuteMultiple).
  • The more specific argument annotation alerts users and developers that the function expects a more specific type of data.

Disadvantages:

  • Depending on your use case, you may need to forward various methods to the data field within your struct. For example, for FiveMinuteMultiple you may need to forward methods such as day, hour, etc, to the t field of the struct.
  • Adding a new concept (type) to represent assertions about your data might introduce an unnecessary layer of abstraction.

You could put your @assert statements in a @debug block. Then the @assert call is desactivated, unless you activate debugging either globally (ENV["JULIA_DEBUG"] = "all") or just for your module (ENV["JULIA_DEBUG"] = "NameOfYourModule")

julia> @debug begin
          @assert 1==2
       end
       #or 
       @debug @assert 1==2 # assert is not called

julia> ENV["JULIA_DEBUG"] = "all" # enable debugging
"all"

julia> @debug begin
          @assert 1==2
       end
┌ Error: Exception while generating log record in module Main at REPL[4]:1
│   exception =
│    AssertionError: 1 == 2
│    Stacktrace:
│     [1] top-level scope at REPL[4]:2
│     [2] top-level scope at logging.jl:319
|     ...
└ @ Main REPL[4]:1
Related