Is Julia comprehension not a function call?

If I parse a list comprehension as such:

  1. The head isn’t a Symbol call but literally a comprehension. Is call for all non-built-in functions?

  2. The first Arg for comprehension Expr isn’t a Symbol but an expression. 1: Expr.
    How do we know which functions have Symbols as their first Arg and which have expressions? And why don’t all functions have Symbols as their first Arg?

  1. call is for function calls. Comprehensions are not a function call (at one level of understanding, you can’t write a new method for comprehension)
  2. The structure of :comprehension expressions is different to :call. But even :call might not have a symbol as their first argument:
julia> dump(:(a.x(1)))
Expr
  head: Symbol call
  args: Array{Any}((2,))
    1: Expr
      head: Symbol .
      args: Array{Any}((2,))
        1: Symbol a
        2: QuoteNode
          value: Symbol x
    2: Int64 1

Perhaps a better question to start with is, What are you trying to do?

1 Like

You might also be interested in looking at the lowered IR. In general most syntax in julia corresponds to function calls, but they don’t show up until lowering:

julia> @Meta.lower [rand() for _ in 1:5, _ in 1:3]
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─       $(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─      global var"#9#10"
│        const var"#9#10"
│   %3 = Core._structtype(Main, Symbol("#9#10"), Core.svec(), Core.svec(), Core.svec(), false, 0)
│        Core._setsuper!(%3, Core.Function)
│        var"#9#10" = %3
│        Core._typebody!(%3, Core.svec())
└──      return nothing
)))
│   %2  = Core.svec(var"#9#10", Core.Any)
│   %3  = Core.svec()
│   %4  = Core.svec(%2, %3, $(QuoteNode(:(#= none:0 =#))))
│         $(Expr(:method, false, :(%4), CodeInfo(
    @ none within `none`
1 ─ %1 = Base.indexed_iterate(@_2, 1)
│        Core.getfield(%1, 1)
│        @_3 = Core.getfield(%1, 2)
│   %4 = Base.indexed_iterate(@_2, 2, @_3)
│        Core.getfield(%4, 1)
│   %6 = rand()
└──      return %6
)))
│         #9 = %new(var"#9#10")
│   %7  = #9
│   %8  = 1:5
│   %9  = 1:3
│   %10 = Base.product(%8, %9)
│   %11 = Base.Generator(%7, %10)
│   %12 = Base.collect(%11)
└──       return %12
))))

There is a bit of junk here to set up the (_,_)->rand() closure, but the key part is at the bottom. You can see the comprehension essentially lowers to:
collect(Base.Generator((_, _)->rand(), Base.product(1:3, 1:3)))

7 Likes

Thank you! What is IR?

intermediate representation: Julia SSA-form IR · The Julia Language

1 Like

Thank you!