# Getting the name of a Struct / Scoping Issues with Custom Structs

**URL:** <https://discourse.julialang.org/t/getting-the-name-of-a-struct-scoping-issues-with-custom-structs/58706>\
**Category:** General Usage\
**Tags:** question, module, scope, struct\
**Created:** [April 6, 2021, 6:20pm UTC](https://discourse.julialang.org/t/getting-the-name-of-a-struct-scoping-issues-with-custom-structs/58706 "2021-04-06T18:20:57Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![mrVeng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrveng/32/8836_2.png) [@mrVeng](https://discourse.julialang.org/u/mrVeng)\
**Post date:** [April 6, 2021, 6:20pm UTC](https://discourse.julialang.org/t/getting-the-name-of-a-struct-scoping-issues-with-custom-structs/58706/1 "2021-04-06T18:20:57Z")

</div>

Hi there,

I have a scoping issues, and did not manage to find a solution on my own. I was hoping that someone more knowledgeable might have a quick solution for this:

1. I define an abstract type `AbstractModel` in a module, and a function `func1` that works with a sub-type of it.
2. After loading this module, I define a subtype `Model <: AbstractModel`. I cannot define this in the module, as I dont know all fields beforehand.
3. The task of `func1` is to output the correct name of `Model` - here is my problem. `Model` is not known when I load the module, and I get an error. MWE:

```julia
module TestModule
## Define Abstract type
	abstract type AbstractModel end
## What I would Like - Define function that works with Model<:AbstractModel
 	function func1(mod::M) where {M<:AbstractModel}
		eval( nameof( typeof(mod) ) )
	end
## Export everything
    export AbstractModel
    export func1
end
using Main.TestModule

struct Model{A,B} <: AbstractModel
	a :: A
	b :: B
end
mod = Model(1., 2)
func1(mod) #UndefVarError: Model not defined - not defined in Module yet
eval( nameof( typeof(mod) ) ) #But easy to do in global scope...

```

Unfortunately, I cannot just use `eval( typeof(mod) )` (which would work), and I need the struct name, as I may need to initiate a new `Model` with different field types (Dual numbers instead of Floats). If I define

```julia
function func2(mod::M) where {M<:AbstractModel}
	eval( typeof(mod) )
end

```

in the module, I actually get a valid result `func2(mod) #Model{Float64, Int64}`. However, I need the name of the struct, not including all the types. Is there any way that I could `func1` get to work?

Best regards,

**Edit** : Alternatively one could easily just get the Symbol `:Model`, but then it costs much more to initiate such a struct because I have to work with `eval(:Model)` every time.

---

<div class="post-metadata">

**Author:** ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)\
**Post date:** [April 6, 2021, 7:28pm UTC](https://discourse.julialang.org/t/getting-the-name-of-a-struct-scoping-issues-with-custom-structs/58706/2 "2021-04-06T19:28:21Z")

</div>

`eval` works within a global scope (each module has its own global scope). Now `Model` is not defined in the module `TestModule`, thus the `UndefVarError`. This works:

```julia
module TestModule
## Define Abstract type
	abstract type AbstractModel end
## What I would Like - Define function that works with Model<:AbstractModel
 	function func1(m, mod::M) where {M<:AbstractModel}
	    Base.eval(m, nameof( typeof(mod) ))
	end

## Export everything
    export AbstractModel
    export func1
end
using Main.TestModule

struct Model{A,B} <: AbstractModel
	a :: A
	b :: B
end
mod = Model(1., 2)
func1(@ __MODULE__ , mod) 

```

Anyhow, this doesn’t look right to me. `eval` is bad, `eval` in another module is worse. As I don’t quite understand your problem, I cannot help though…

---

<div class="post-metadata">

**Author:** ![chris-b1](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chris-b1/32/14165_2.png) [@chris-b1](https://discourse.julialang.org/u/chris-b1)\
**Post date:** [April 6, 2021, 8:38pm UTC](https://discourse.julialang.org/t/getting-the-name-of-a-struct-scoping-issues-with-custom-structs/58706/3 "2021-04-06T20:38:29Z")

</div>

I certainly could be mis-understanding your MWE, but I don’t think you need eval at all. `func1` already knows what `M` is through the type parameter - you can stringify, construct new values, etc with it.

```julia
# inside `TestModule`
function func1(mod::M) where {M<:AbstractModel}
    println("type name is $(string(M))")
    return M(1.0, 3)
end

# outside
julia> func1(mod)
type name is Model{Float64, Int64}
Model{Float64, Int64}(1.0, 3)

```

---

<div class="post-metadata">

**Author:** ![mrVeng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrveng/32/8836_2.png) [@mrVeng](https://discourse.julialang.org/u/mrVeng)\
**Post date:** [April 6, 2021, 8:56pm UTC](https://discourse.julialang.org/t/getting-the-name-of-a-struct-scoping-issues-with-custom-structs/58706/4 "2021-04-06T20:56:55Z")

</div>

> [@mauro3](#):
>
> `func1(@ __MODULE__ , mod`

Thank you so much for your reply! Your function works perfectly in the example, but I was not explaining it very well, apologies for that. And yes I agree that this approach is not ideal, but for now this would be a quick fix for a problem I am facing.

What I would like to do is the following: starting from a `Vector{Float64}` `θ`, create a new `TestModel` via `SuperModel.name(θ...)` (MWE below). I have a working version where I only create a mutable TestModel once and inplace replace the fields instead of creating a new Model - here I also do not need to use eval or check for struct names. However, I do have to use Automatic Diff libraries on such structs, so the types for A and B in `TestModel{A,B}` would be Reals, and thus no fully typed if I wanted an inplace version for this.

Instead of that, I just create new parametric struct where A and B are fully typed (until Zygote is faster or I find an AD library that does not change types of my input) with the corresponding input. This allocates memory but the calculations that I am performing with the fully typed `TestModel` afterwards are much faster than if I would use a version with not abstract typed fields.

**Question 1:** I wrote down a more realistic code of my problem below. `@ __MODULE__ ` works when I call it within the script, is there something similar if I would call it from within the module as below?

```julia
module TestModule
## Define Abstract type
	abstract type AbstractModel end
## Define function that works with Model<:AbstractModel
	function func1(m, mod::M) where {M<:AbstractModel}
		Base.eval(m, nameof( typeof(mod) ) )
	end
## Define Super Model
	struct SuperModel{M<:AbstractModel, B}
		model :: M
		name :: B
		function SuperModel(model::M) where {M<:AbstractModel}
			name = func1(@ __MODULE__ , model)
			new{M, typeof(name)}(model, name)
		end
	end
## Export everything
	export func1, AbstractModel, SuperModel
end
using Main.TestModule

struct TestModel{A,B} <: AbstractModel
	a :: A
	b :: B
end
mod = TestModel(1., 2)

#Problem here contintues for me as I would call the function from the module.
SuperModel(mod) #UndefVarError: TestModel not defined

```

**Question 2:** Performance wise I was under the impression, that making a new instance of a `TestModel` was as fast via `Supermodel.name` as via directly calling `TestModel`, but somehow the latter approach seems faster here? Maybe I used more fields for my previous benchmarks, or the compiler is too clever here:

```julia
using BenchmarkTools
mod = TestModel(randn(5), randn(5))
mod_name = eval( nameof( typeof( mod ) ) )
a = randn(5)
b = randn(5)
@btime TestModel($a, $b) #1 - 1.100 ns (0 allocations: 0 bytes) Seems wrong
@btime mod_name($a, $b) #2 - 85.595 ns (1 allocation: 32 bytes)
@btime eval( nameof( typeof( mod ) ) )( $a, $b ) #3 - 156.784 ns (1 allocation: 32 bytes)

```

Thank you for your time! I hope this makes a bit more sense now.

**Edit:** I can adjust the inner constructor to:

```julia
## Define Super Model
	struct SuperModel{M<:AbstractModel, B}
		model :: M
		name :: B
		function SuperModel(model::M) where {M<:AbstractModel}
			name = func1(Main, model)
			new{M, typeof(name)}(model, name)
		end
	end

```

to make it work. But `func1(Main, model)` sounds dangerous even for a temporary fix. Is there a safer variant to remember the Model name in order be able to call `TestModel($a, $b)` just as fast as I would directly call it?
