"`const static`" function parameters

Sometimes you just want to put a fat array inside a function. The macro posted above is a bit of a hack but it does the job.

@ChrisRackauckas What if it is a constant matrix? You don’t want to expand it into components a1, ... because you want to exploit matrix multiplication.

1 Like

FWIW, you can boil this down to:

macro static(ex)
  eval(ex)
end

:slight_smile: (though you might strictly need a quotenode or something if ex could be an expression)

It is indeed pretty weird to want to do this, but just for fun:

using MacroTools

macro static(ex)
  @capture(ex, name_::T_ = val_) || error("invalid @static")
  ref = Ref{eval(T)}()
  set = Ref(false)
  :($(esc(name)) = if $set[]
      $ref[]
    else
      $ref[] = $(esc(ex))
      $set[] = true
      $ref[]
    end)
end

function test(n)
  @static pi::Float64 = 2n
  return pi
end

test(6) # 12.0
test(3) # also 12.0
4 Likes

@MikeInnes I propose that something like this be included in Base. Don’t you think?

@MikeInnes Why is the llvm code so messy?

@code_llvm test(6)

define double @julia_test_71387(i64) #0 {
top:
%1 = load i8, i8* inttoptr (i64 139707517794576 to i8*), align 16
%2 = and i8 %1, 1
%3 = icmp eq i8 %2, 0
br i1 %3, label %L, label %L1

L: ; preds = %top
%4 = shl i64 %0, 1
%5 = sitofp i64 %4 to double
store double %5, double* inttoptr (i64 139707517794560 to double*), align 256
store i8 1, i8* inttoptr (i64 139707517794576 to i8*), align 16
br label %L1

L1: ; preds = %top, %L
%“#temp#.sroa.0.02” = load double, double* inttoptr (i64 139707517794560 to double*), align 256
ret double %“#temp#.sroa.0.02”
}

I suspect it’s not widely useful enough to have in base; but the good news is that if it doesn’t require language support, there’s no real downside to having it in a package.

The LLVM code isn’t going to be as clean as the “really static” version because you have to do pointer lookups and branches to initially set the variable. Such is the cost of being dynamic, I’m afraid.

Depends on how big it is. It may be a good idea to skip BLAS if the constant matrix is small enough. But yes, the right approach all depends on the problem, which I do not know. I was just suggesting other approaches.

Another way you might want to handle this without polluting any good names in the global namespace is to use a more specific global name, and alias it in the function. Like

const GLOBAL_PI = ...

and then in the function, just

pi = GLOBAL_PI

I dont think it’s weird. This is how local static variables work in C. I think it would also reduce a lot of the use cases for global variables. IMHO, static local vars would be better than globals.

1 Like

This reminds me of a situation I encounted the other day in which some kind of global variable would be convenient. I had an array of objects that needed a common variable. In some languages, its possible to do something like this:

mutable myStruct
    global x::Int
    y::Int
end

M = [myStruct() for i in 1:100]

myStruct.x = 1

The field x would be the same across all elements in M and could be updated simultaneously. The field y, on the other hand, could be unique for each element in M. Is it possible to achieve this with a variation of your code (it does not appear to work as is)?

Does this work?

myfun = let
       const x = rand(2,2)
       function myfun()
       x
       end
       end
myfun()

How would you pass arguments with this syntax?

julia> myfun = let
       const x = rand(2,2)
       function myfun(y)
       x+y
       end
       end
myfun (generic function with 2 methods)

julia> myfun(5)
2×2 Array{Float64,2}:
 5.09896  5.08873
 5.34033  5.04235

Yep, that is very nice. It seems to have exactly the behavior I want. The code looks a bit ugly, but it gets the job done. If one could accomplish this with a macro that would be nice. I have a very limited understanding of macros, so it is not 100% clear to me if the macros suggested above accomplish this.

Aside, I don’t think the const is needed as x is in a local scope and not global.

This is probably nicer, I’m not sure if the const is neccesary here.

julia> module HidingModule
       export myfun
       const x = rand(2,2)
       myfun(y) = x+y
       end
HidingModule

julia> using HidingModule

julia> myfun(6)
2×2 Array{Float64,2}:
 6.74679  6.51266
 6.33858  6.27355

Yes, global is needed then as x is in the global scope of the HidingModule. I’d say the most idiomatic is

let
    x = rand(2,2)
    global function myfun(y)
       x+y
    end
end
4 Likes

There is another issue tracking this.
https://github.com/JuliaLang/julia/issues/15056

The thing I don’t like about this is that it messes with the natural identation of the code. Functions are supposed to be without identation, but the let block forces you to indent (otherwise you could look at the x = rand(2,2) and not realize what it is doing out there). The only downside is readability.

Nope. Functions should be indented like everything else at the same level, eg for a closure

function add_this(x)
    function(y)
        x+y
    end
end

The indentations above are correct: they are inside a let block, and everything else at the same level would be indented by the same amount.

I meant global functions. In your example, the closure is not callable from the outside.