Workflow when experimenting with types

Hi all, longtime lurker and irregular Julia user here.

Revise is absolutely great and I love it but I wonder if there’s a better workflow when experimenting with types.

I try to get my types defined ASAP but especially in the beginning, when taking a stab at a particular problem I frequently find myself changing definitions. Currently I just restart the interactive in order to reload Revise and my type definitions but I feel like I’m doing something wrong or that there might be a better way.

Here’s what such a session looks like in summary:

(start julia)
pkg> activate .
julia> using Revise
julia> using ThePackageImWorkingOn
(some experimentation on the julia prompt and then realize, darn my struct is wrong)
julia> exit()
(fix code, restart julia)
pkg] activate . # etc (repeating from top)

Maybe I’m not thinking meticulously enough about my structures, that’s a valid point. If that’s the case I’d love to know about it too.

Anyway, consider this a intro post as well, I’m perhaps not a typical Julia user since my field is not academics but I love how the language makes me think of problems differently from a programmer point of view.

Kind regards,

Bas

1 Like

I usually use search and replace in my project MyStructMyStruct2, etc. (think of it at different version of the struct). Once I’m done I rename it MyStruct and restart.

1 Like

There are some workarounds, like this:

Basically you can use named tuples until you have decided the final fields of the structs.

4 Likes

To be honest, I didn’t even consider macros and the idea of using named tuples instead didn’t really occur with me either. Julia doesn’t have enough parenthesis to force me into that macro-thinking mode I guess. This seems like a great workaround when just trying out things, even if just for the ideas. I will experiment with it, thanks!

The pluto notebook allows you to redefine types.

2 Likes

From the demos I’ve seen Pluto looks absolutely amazing. I will definitely give it a try.

1 Like

I haven’t used that a lot. Maybe one nice way to use it is to define something like:

using ProtoStructs

@proto MyType

MyType(
   a::Int=1,
   b::Int=2,
   c::String="abc"
  ) = MyType( 
         a=a,
         b=a,
         c=c)

Then you have some common constructors working:

julia> x = MyType()
MyType{NamedTuple{(:a, :b, :c),Tuple{Int64,Int64,String}}}((a = 1, b = 1, c = "abc"))

julia> x.a, x.c
(1, "abc")

julia> x = MyType(3,3,"ddd")
MyType{NamedTuple{(:a, :b, :c),Tuple{Int64,Int64,String}}}((a = 3, b = 3, c = "ddd"))

julia> x.c
"ddd"

and, finally, when you decide what fields you will keep, if you use the Parameters package the conversion is very easy:

using Parameters
@with_kw struct MyType
   a::Int=1
   b::Int=2
   c::String="abc"
end

Another option I’ve used is to put the code I’m developing into a module and then repeatedly include() that module’s file. This creates a new module with the same name (and all your modified type definitions inside it), so redefining types is not a problem. The biggest downside is that it can create a confusing situation if you have any old values left over from the previous iteration of your module, since they will have the same module name as your current values but incompatible types.

7 Likes

But you can’t export struct names, right? At least I remember that in this case compiler complained about const redefined. So you have to use it as MyModule.A not just A

1 Like

That’s right. Not ideal, I agree.

1 Like