# Help with macro to construct truth tables

**URL:** <https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610>\
**Category:** General Usage\
**Tags:** macros\
**Created:** [May 21, 2021, 10:35pm UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610 "2021-05-21T22:35:12Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [May 21, 2021, 10:35pm UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610/1 "2021-05-21T22:35:12Z")

</div>

I’m trying to write a macro to generate truth tables. So far I’ve had success with finding the variables and evaluating the expression. However, I haven’t been able to display the truth value for individual variables.

The code I have so far is the following:

```julia
macro truth_table(expr)
    # Walk the expression and push the variables found to a vector `vars`:
    vars = Vector{Symbol}([])
    find_vars!(var::Symbol) = !(var in vars) && push!(vars, var)
    find_vars!(expr::Expr) = find_vars!.(expr.args)
    find_vars!(::Bool) = nothing

    find_vars!(expr)

    # Generate nested for loops where each variable is given a truth value:
    ex = :(@show $vars, $expr) # <- the problematic line (I think?)
    for i in reverse(vars) # Take a variable (in the order they were found).
        ex = quote
            for $i in [true, false] # Iterate over truth values.
                $ex # Nest the previous expression.
            end
        end
    end
    return ex
end # truth_table

```

This will correctly evaluate truth values for arbitrary boolean expressions, but in the REPL it’ll print as:

```julia
julia> @truth_table x && y
([:x, :y], x && y) = ([:x, :y], true)
([:x, :y], x && y) = ([:x, :y], false)
([:x, :y], x && y) = ([:x, :y], false)
([:x, :y], x && y) = ([:x, :y], false)

```

I would expect the RHS to be `([true, true], true)`, `([true, false], false)` and so on.

My question is: how can I show the evaluated value for each variable? I know `vars` is an array of symbols, but I’d like to get whatever the symbol evaluates to. What should I write in the intial `ex` instead?

Ideally I would like to save the table in a NamedArray where the column names are the names of each variable, but for now I’m just trying to get it to print correctly.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [May 21, 2021, 10:52pm UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610/2 "2021-05-21T22:52:45Z")

</div>

Hello @savq and welcome.

You’re much more likely to get help if you show us a minimal working example of the proble, you’re facing. [Please read: make it easier to help you](https://discourse.julialang.org/t/psa-make-it-easier-to-help-you/14757)

In this case, if you could make a simple example of the code that produces the problem that we can actually run on our machines, it’s much easier to help.

That said, I think in this case I can guess what the problem is even though the code you posted doesn’t actually have the error. The problem is essentially a difference between this macro:

```julia
julia> macro foo(args...)
           esc(:(($(args...),)))
       end
@foo (macro with 1 method)

```

and this macro:

```julia
julia> macro bar(args...)
           esc(:(($(args...,))))
       end
@bar (macro with 1 method)

```

Here’s the difference:

```julia
julia> x, y = 1, 2
(1, 2)

julia> @foo x y
(1, 2)

julia> @bar x y
(:x, :y)

```

Now, where this mistake may have been made in your code is anyone’s guess.

---

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [May 21, 2021, 11:16pm UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610/3 "2021-05-21T23:16:46Z")

</div>

(The snippets I posted were the whole code. I edited it to make it clearer)

I’m not sure it’s an escaping problem. To be clear, the variables don’t have to be defined before a call to `@truth_table` is made.

The macro can do this:

```julia
julia> isdefined(Main, :p)
false

julia> isdefined(Main, :q)
false

julia> isdefined(Main, :r)
false

julia> @truth_table p || (q && r)
([:p, :q, :r], p || q && r) = ([:p, :q, :r], true)
([:p, :q, :r], p || q && r) = ([:p, :q, :r], true)
([:p, :q, :r], p || q && r) = ([:p, :q, :r], true)
([:p, :q, :r], p || q && r) = ([:p, :q, :r], true)
([:p, :q, :r], p || q && r) = ([:p, :q, :r], true)
([:p, :q, :r], p || q && r) = ([:p, :q, :r], false)
([:p, :q, :r], p || q && r) = ([:p, :q, :r], false)
([:p, :q, :r], p || q && r) = ([:p, :q, :r], false)

```

So the context of the caller doesn’t really matter. The problem is mainly that I’m creating the loops from the inside out so escaping in `ex` would result in an outerref that doesn’t exist yet. I’m trying to find an alternative to that.

---

<div class="post-metadata">

**Author:** ![Wei\_Yang](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wei_yang/32/23951_2.png) [@Wei\_Yang](https://discourse.julialang.org/u/Wei_Yang)\
**Post date:** [May 21, 2021, 11:20pm UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610/4 "2021-05-21T23:20:43Z")

</div>

Just some idea, the truth table for `and` is just a Boolean multiplication table

```julia
V=[false, true]
V.*V'

2×2 BitMatrix:
 0 0
 0 1

```

and for `or `

```julia
⊕(x,y) = max(x,y)
V.⊕V'

2×2 BitMatrix:
 0 1
 1 1

```

Not sure if this is of any help.

```julia
p = [fill(false,4)...,fill(true,4)...]
q = repeat([false,true],4)
r = repeat([false,false,true,true],2)

hcat(p,q,r,p.⊕(q.*r))

8×4 Matrix{Bool}:
 0 0 0 0
 0 1 0 0
 0 0 1 0
 0 1 1 1
 1 0 0 1
 1 1 0 1
 1 0 1 1
 1 1 1 1

```

😥 sorry that I’m not using macros, just some idea.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [May 22, 2021, 2:13am UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610/5 "2021-05-22T02:13:49Z")

</div>

> [@savq](#):
>
> (The snippets I posted were the whole code. I edited it to make it clearer)

Sorry, for the misunderstanding, I thought there was a missing chunk.

> [@savq](#):
>
> I’m not sure it’s an escaping problem. To be clear, the variables don’t have to be defined before a call to `@truth_table` is made.

I wasn’t talking about escaping, I was talking about the difference between

```julia
:(($(args...),))

```

and

```julia
:(($(args...,)))

```

To in your code, the problem is that when you write

```julia
:(@show $vars, $expr)

```

this doesn’t do what you want:

```julia
julia> let vars = [:x, :y], expr=:(x && y)
           :(@show $vars, $expr)
       end
:(#= REPL[91]:2 =# @show ([:x, :y], x && y))

```

here we see there’s a vector of `Symbol`s in here instead of what you wanted. This can be re-written as

```julia
julia> let vars = [:x, :y], expr=:(x && y)
           :(@show [$(vars...)], $expr)
       end
:(#= REPL[92]:2 =# @show ([x, y], x && y))

```

to now get a vector of `x` and `y`. Though, for efficiency, you likely are better off writing

```julia
julia> let vars = [:x, :y], expr=:(x && y)
           :(@show ($(vars...),), $expr)
       end
:(#= REPL[93]:2 =# @show ((x, y), x && y))

```

to get a `Tuple` of values instead.

Here’s the revised macro:

```julia
julia> macro truth_table(expr)
           # Walk the expression and push the variables found to a vector `vars`:
           vars = Vector{Symbol}() # the `[]` wasn't necessary here.
           find_vars!(var::Symbol) = !(var in vars) && push!(vars, var)
           find_vars!(expr::Expr) = find_vars!.(expr.args)
           find_vars!(::Bool) = nothing

           find_vars!(expr)

           # Generate nested for loops where each variable is given a truth value:
           ex = :(@show ($(vars...),), $expr)  
           for i in reverse(vars) # Take a variable (in the order they were found).
               ex = quote
                   for $i in (true, false) # Iterate over truth values.
                       $ex # Nest the previous expression.
                   end
               end
           end
           return ex
       end # truth_table
@truth_table (macro with 1 method)

```

Now, we have

```julia
julia> @truth_table x && y
((x, y), x && y) = ((true, true), true)
((x, y), x && y) = ((true, false), false)
((x, y), x && y) = ((false, true), false)
((x, y), x && y) = ((false, false), false)

```

and

```julia
julia> @truth_table p || (q && r)
((p, q, r), p || q && r) = ((true, true, true), true)
((p, q, r), p || q && r) = ((true, true, false), true)
((p, q, r), p || q && r) = ((true, false, true), true)
((p, q, r), p || q && r) = ((true, false, false), true)
((p, q, r), p || q && r) = ((false, true, true), true)
((p, q, r), p || q && r) = ((false, true, false), false)
((p, q, r), p || q && r) = ((false, false, true), false)
((p, q, r), p || q && r) = ((false, false, false), false)

```

---

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [May 22, 2021, 2:40am UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610/6 "2021-05-22T02:40:31Z")

</div>

> I wasn’t talking about escaping…

Ah, then I misunderstood. I missed the syntactic difference there. But I get know.

Thanks a lot!

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [May 22, 2021, 2:46am UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610/7 "2021-05-22T02:46:33Z")

</div>

Happy to help

---

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [May 22, 2021, 2:52am UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610/8 "2021-05-22T02:52:05Z")

</div>

> Just some idea, the truth table for `and` is just a Boolean multiplication table

Yes, I was mostly interested in the meta-programming aspect of the problem tho 🙂

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [May 22, 2021, 3:45am UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610/9 "2021-05-22T03:45:55Z")

</div>

> [@Wei\_Yang](#):
>
> Just some idea, the truth table for `and` is just a Boolean multiplication table
> 
> ```julia
> V=[false, true]
> V.*V'
> 
> 2×2 BitMatrix:
> 0 0
> 0 1
> 
> ```
> 
> and for `or `
> 
> ```julia
> ⊕(x,y) = max(x,y)
> V.⊕V'
> 
> 2×2 BitMatrix:
> 0 1
> 1 1
> 
> ```

By the way, you can just use `&` and `|` directly.

```julia
julia> lift(v, N=1) = reshape(v, ntuple(_ -> 1, N)..., size(v)...)
lift (generic function with 2 methods)

julia> p, q, r = [true, false], lift([true, false]), lift([true, false], 2);

julia> p .& q
2×2 BitMatrix:
 1 0
 0 0

julia> p .| q
2×2 BitMatrix:
 1 1
 1 0

julia> p .| (q .& r)
2×2×2 BitArray{3}:
[:, :, 1] =
 1 1
 1 0

[:, :, 2] =
 1 1
 0 0

```

---

<div class="post-metadata">

**Author:** ![Wei\_Yang](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wei_yang/32/23951_2.png) [@Wei\_Yang](https://discourse.julialang.org/u/Wei_Yang)\
**Post date:** [May 22, 2021, 3:46am UTC](https://discourse.julialang.org/t/help-with-macro-to-construct-truth-tables/61610/10 "2021-05-22T03:46:48Z")

</div>

😂 this is definitely better
