Read the example more carefully; you don’t need to specify default values (missing values will just error).
julia> Base.@kwdef struct Foo
julia> Foo(a = 1, b = 2)
ERROR: UndefKeywordError: keyword argument a not assigned
 Foo() at ./util.jl:673
 top-level scope at none:0
I also struggled with this.
Not so much that the constructor looked ugly, but rather that maintaining the code, especially calling the constructor, was easy to get wrong when rearranging, adding and deleting fields (which I do often).
So I wrote a macro to make this a bit easier.
dataType = eval(current_module(), T)
esc(Expr(:call, T, fieldnames(dataType)...))
(Note: macro works on v0.6, probably need modifying to work with v1.0)
Essentially, the constructor needs to define a local variable with the same name for each field in the struct.
Order of variable definitions doesn’t matter.
So your constructor becomes:
field = open(fieldPath) do file
shader = genFieldComputeShader(field)
renderer = GLShader("MeshRenderer", "./vertex.glsl", "./fragment.glsl", ["\$df" => field])
pointcloud, cmdBuffer, pointcloudVAO = computePointCloud(computer)
errorComputer = GLShader("MeshDistanceErrorComputer", "./computeMeshError.glsl", ["\$df" => field])
mesh, normals, meshBuffer = computeMeshFromPointCloud(cmdBuffer, pointcloud)
Note I had to change local variable computer to shader to match the field name.
Inner constructors should be used sparingly cf the docs
It is considered good form to provide as few inner constructor methods as possible: only those taking all arguments explicitly and enforcing essential error checking and transformation. Additional convenience constructor methods, supplying default values or auxiliary transformations, should be provided as outer constructors that call the inner constructors to do the heavy lifting.