Set Id of struct on construction

Hi,

I’m trying to implement a struct that must have an Id, which is set on construction, similar to C# static member

So far, I have been able to do it like this:

x = 0

struct A
    prop1
    id
    A(prop1) = begin
        global x+=1
        new(prop1, x)
    end
end

But I think there should be a better way.

Maybe a closure like

let x::Int = 0
    struct Foo
        x::Int
        function Foo()
            x += 1
            new(x)
        end
    end
end

(note: not thread safe in this form).

4 Likes

Building an array of structs would not suffice, with the generation of new instances done using push! to that array? I do not know if that can be adapted to your problem, but seem much safer than updating a global variable.

If that is not good, I do not see much escaping from what you have done. Except that I perhaps would use a counter that is constant with mutable fields, to avoid messing up with other variables:

julia> struct A
          id
          x
       end

julia> function Generate_A_and_Count!( x, counter )
         counter[1] +=1 
         A(counter[1],x)
       end
Generate_A_and_Count (generic function with 1 method)

julia> const A_counter = [0]

julia> A(x) = Generate_A_and_Count!(x,A_counter)
A

julia> A(1.)
A(1, 1.0)

julia> A(1.)
A(2, 1.0)


Maybe you can assign a UUID to your struct. Something like

julia> using UUIDs

julia> struct Mytype{T}
           prop1::T 
           id::UUID
           Mytype(prop1::T) where T = new{T}(prop1, uuid4())
       end 

julia> obj = Mytype(1.) 
Mytype{Float64}(1.0, UUID("9bc25060-6548-44ff-9422-52d63fe77ed7"))

But, now I realized that id here is not the total count of the instances of Mytype :slight_smile: I do not know, this approach may still be useful, though.

1 Like

This is interesting, but I do not understand it. How come the value of x is visible and updated among successive calls of Foo()? In what scope is x?

x exists in the scope of the let block and in the scope of any function defined within that block that close over the variable of x.

2 Likes

Probably I should be starting a new thread, but why in the example below A() is available in the global scope, but f() is not?

julia> let 
         struct A
           x
         end
         A() = A(1)
       end
A

julia> A()
A(1)

julia> let 
         f() = 1
       end
(::var"#f#1") (generic function with 1 method)

julia> f()
ERROR: UndefVarError: f not defined

Given that, are let blocks a reasonable choice to define functions (objective functions for optimization solvers, for example), with parameters? Something like:

julia> let 
         struct f end
         a = 1
         f(x) = (x-a)^2
       end

julia> f(3)
4

Here f is a constructor of the type f (needed for f to be available in the global scope), and a is a constant parameter of the function. Using a struct like that seems ugly, but would that work?

Let blocks create a local scope. Within local scopes when functions are defined Julia makes them local to that scope unless instructed to do otherwise.

For example:

let 
  global f() = 1
end

will be callable outside of the let block.

1 Like

https://docs.julialang.org/en/v1/manual/variables-and-scoping/#Let-Blocks

Composite type names (struct, mutable struct) end up in global scope. (The scope chapter could clarify this a bit more IMO).

1 Like

Thank you!, I liked the uuid and let approaches, they are very useful solutions for different use cases.