Initialize a variable inside a struct

I am not a very good Julia programmer (despite reading the doc numerous times).
For what I am trying to code at the moment, I am trying to create something close to a Python class.

I thought of using a mutable struct, but I realized that one cannot initialize a variable as one of it’s argument. Something like

mutable struct Test
a
b
c = 3
end

returns the error

ERROR: syntax: "c=3" inside type definition is reserved
Stacktrace:
 [1] top-level scope at none:0

Ultimatively what I want to do would be something like a

Class Test
    def __init__(self, var1, var2):
         self.var1 = var1
         self.var2 = var2
         self.matrix = foo(shape(var1))

And I don’t know how to do this in Julia if not for declaring matrix as a const outside of the struct which would be unpracticle.
I have already read the doc on constructors and composite types so please do not just tell me to “read the doc again”.

mutable struct Test
a::Float64
b::String
c::Int
end

test_object = Test(1.0, "Test", 1)

You don’t have Classes in Julia. Instead you first define the data structure (struct, e.g. what kind of data your objects should hold) and what type of data the structure will hold. Afterwards you create instances of those “objects” with concrete values for the variables (could also be variables from somewhere else).

You can then access this data directly if you want to:

julia> test_object.a
1.0

julia> test_object.b
"Test"

julia> test_object.c
1

Does this help you?

1 Like

Ah I see you’ve changed your question a bit since I first read it.

Now I would personally just do the following things to do what you want:

  • create a struct and add a field in the struct that is able to hold the desired matrix, i.e. has the appropriate type (e.g. c::Array{Int64, 2} or whatever you need)
    mutable struct Test
    a::Int
    b::Int
    c::Array{Int, 2}
    
  • create an anonymous function that creates the desired matrix (e.g. foo(a, b) = [a b])
  • initialise the object = Test(1, 2, [0 0]) with an empty Array for c
  • update the array to reflect what you want it to be (e.g. object.c = foo(object.a, object.b))

However, there might be better ways to do it.

Edit: Instead of writing [0 0] you could also write zeros(Int, 1, 2). This would be the preferred way to do it once your matrices become a lot bigger.

That is actually how I wanted to do it, but it feels a bit sloppy and forces me to include an extra input variable unpon instanciation so I thought maybe there is a better way to do it. Thanks for your help.

Yes my fnger slipped and pressed enter before I could properlly finish writting my question that’s why !

No worries, it’s not as if you could have anticipated my eagerness to help. :smiley: Normally it’s no problem to rewrite parts of your question before people find the time to answer.

If there’s a better way to do what you want to do, there will surely be somebody coming along to tell us how.

You can define an outer constructor. Something like:

struct Foo
    var1
    var2
    matrix
end

Foo(a,b) = Foo(a,b,zeros(a,b))

or an inner constructor:

struct Foo
    var1
    var2
    matrix
    Foo(a,b,m=zeros(a,b)) = new(a,b,m)
end

or, if you want to enforce a specific value for the matrix (not allow the caller to override it),

struct Foo
    var1
    var2
    matrix
    Foo(a,b) = new(a,b,zeros(a,b))
end
1 Like

The Base.@kwdef macro allows you to use the c = 3 syntax:

julia> Base.@kwdef mutable struct Test
         a
         b
         c = 3
       end
Test

julia> t = Test(a=1, b=2)
Test(1, 2, 3)
5 Likes

I think this is actually the solution to OP’s question. Way more in line with what they were hoping to be able to write.

3 Likes

Wow! This answered I question that (1) I didn’t know I had and (2) if I’d known I had it I did not have the vocabulary to ask it.

Thanks.

2 Likes

Does Base.@kwdef work in Julia 1.0? I stuck that in something I’m working on and CI failed on 1.0. It was happy on 1.4, 1.5, and the nightly.

The problem might be elsewhere, like a few fresh BLAS calls.

I tested in a copy I had lying around (1.0.5), and it works. Just note that the constructor becomes: t = Test(a=1, b=2) not t = Test(1, 2) (i.e., if you do not want to pass the fields with default value, you need to pass the other fields as named arguments, the positional constructor keeps existing and expecting three arguments).

For a more featureful version of @kwdef, you can also check out https://github.com/mauro3/Parameters.jl

2 Likes

Thanks @Henrique_Becker and @rdeits.

It seems my issues are a bit deeper than that. I’m using the mp! from 1.3 now and, having heard “The State of Julia” will be moving to 1.5-dependent things as soon as it’s released.

So, my 1.0 days are behind me. I need the non-allocating array slices bad. Using those will clean up my code in a significant way. I’m also happy to hear about the Givens rotations.

Does this hep?

function shape(x)
    return 3*x
end

struct Test
   a :: Int64
   b :: Int64
   c :: Int64
   function Test(x, y)
      return new(x, y, shape(x))
   end
end

julia> Test(1, 2)
Test(1, 2, 3)

Or even simpler:

shape(x) = 3*x

struct Test
   a :: Int64
   b :: Int64
   c :: Int64
end

Test(x, y) = Test(x, y, shape(x))

julia> Test(1, 2)
Test(1, 2, 3)
1 Like