# Type Sudoku (test if object is a struct)

**URL:** <https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139>\
**Category:** Performance\
**Tags:** type, dispatch\
**Created:** [August 10, 2021, 2:51pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139 "2021-08-10T14:51:11Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 10, 2021, 2:51pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/1 "2021-08-10T14:51:12Z")

</div>

This is **NOT** to complain about Julia, rather a learning experience, so bear with me…

Dispatching to a struct type seems like the match of the last resort - after a lot of not this, not that, not really testable. And contrary to Sudoku, one never knows if it can be solved.

I got great help [here](https://discourse.julialang.org/t/how-to-dispatch-this-serializer-function/65981/32) putting multiple dispatch to work, still think a test if an object is a structure is necessary.

The following serializer code iterates through an object’s hierarchy and whenever it sees homogeneous data, writes it out. The prefixes with type and dimension data are omitted here.  
I feel this can be made better.

```julia
function isstruct(v) 
    if applicable(fieldnames, typeof(v)) 
        (fieldcount(typeof(v)) > 0) && !(typeof(v)<:Tuple) # for Array_of_Any
    else
        false
    end
end

Writables = Union{<:Real, Char, String, 
               AbstractArray{<:Real}, 
               AbstractArray{Char}, 
               AbstractArray{String}}

function serial(v)
    tv = typeof(v)
    if tv <: Writables
        println(v)
        println("---")
    elseif isstruct(v)
        println("(Struct)")
        for name in fieldnames(tv)
            println("$(name)")
            serial(getfield(v, name));
        end
    elseif tv<:AbstractArray || tv <: Tuple
        println("(Array/Tuple)")
        v1 = first(v)
        if isstruct(v1) 
            println("(Struct)")
            for name in fieldnames(typeof(v1))
                println("$(name)")
                serial(getfield.(v, name));
            end
            # todo: array of homogeneous tuple
        else
            serial.(v);
        end
    else
        error("unknown type: $tv")
    end
end

# Test data
Array_of_Int = [1 ,2]
Array_of_Tuple = [(1, 2), (2, 3)]
Array_of_Any = [(1, 2), "Ab"]

mutable struct Coords
    x::Float64
    y::Float64
    z::Float64
end
Coords() = Coords(rand(), rand(), rand())
Array_of_Struct = [Coords() for i in 1:5]
Single_Struct = Coords()

# Test
println("Test Array_of_Int")
serial(Array_of_Int)
println()
println("Test Array_of_Tuple")
serial(Array_of_Tuple)
println()
println("Test Array_of_Any")
serial(Array_of_Any)
println()
println("Test Single_Struct")
serial(Single_Struct)
println()
println("Test Array_of_Struct")
serial(Array_of_Struct)

```

Output:

```julia
Test Array_of_Int
[1, 2]
---

Test Array_of_Tuple
(Array/Tuple)
(Array/Tuple)
1
---
2
---
(Array/Tuple)
2
---
3
---

Test Array_of_Any
(Array/Tuple)
(Array/Tuple)
1
---
2
---
Ab
---

Test Single_Struct
(Struct)
x
0.9220727028581359
---
y
0.6295692430403457
---
z
0.023560089621190716
---

Test Array_of_Struct
(Array/Tuple)
(Struct)
x
[0.834535060962323, 0.340497217736315, 0.7824763128066028, 0.8189685607684121, 0.9859094096185965]
---
y
[0.4761584369317642, 0.4352515300190545, 0.14527270184939045, 0.6024455919678469, 0.6632760955815888]
---
z
[0.28120117129446975, 0.19801990106458578, 0.03265890208397537, 0.6550273380547817, 0.04383975637217996]
---

```

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [August 10, 2021, 3:13pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/2 "2021-08-10T15:13:50Z")

</div>

I think the issue is that a MATLAB struct is not the same thing as a Julia struct. The Julia `struct` keyword is more closely related to MATLAB’s `classdef`. And _most_ things will be structs in Julia.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [August 10, 2021, 3:24pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/3 "2021-08-10T15:24:39Z")

</div>

> [@Bardo](#):
>
> Dispatching to a struct type

[https://docs.julialang.org/en/v1/manual/types/#Composite-Types](https://docs.julialang.org/en/v1/manual/types/#Composite-Types)

> Composite types are introduced with the struct keyword followed by a block of field names

almost everything you see that are not in `Base` or some low level package are gonna be struct type.

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 10, 2021, 3:25pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/4 "2021-08-10T15:25:19Z")

</div>

Yes sure it is not, and almost everything in Julia is a struct.  
I have tried to understand how the established Julia serializer packages traverse the object tree.  
Was then a too big jump in complexity. If we want to become Julia more accepted (especially by the Matlab folks), we have to fill exactly these gaps.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [August 10, 2021, 3:26pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/5 "2021-08-10T15:26:47Z")

</div>

> [@Bardo](#):
>
> fill exactly these gaps.

what gaps? From what I can tell, Julia is much more flexible and expressive than MATLAB. You don’t want to re-invent stuff to go back to MATLAB

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 10, 2021, 3:30pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/6 "2021-08-10T15:30:24Z")

</div>

Yes, and can easily hard coded for a small number of known objects.  
The problem here is the general case. Given an arbitrary Julia object, traverse and print all data.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [August 10, 2021, 3:31pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/7 "2021-08-10T15:31:51Z")

</div>

> [@Bardo](#):
>
> Given an arbitrary Julia object, traverse and print all data.

doesn’t sound like something well defined but you can get most job done with:

> **[GitHub - JuliaCollections/AbstractTrees.jl: Abstract julia interfaces for...](https://github.com/JuliaCollections/AbstractTrees.jl)**
>
> Abstract julia interfaces for working with trees. Contribute to JuliaCollections/AbstractTrees.jl development by creating an account on GitHub.

Basically you teach it what’s “children” given an object, and it will keep going traversing

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 10, 2021, 3:42pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/8 "2021-08-10T15:42:38Z")

</div>

No no! The gap is not with Julia. There is a gap for people transiting from Matlab to Julia. They (we) are often not trained programmers and experience a kind of complexity shock.  
There is no intention to model Julia back to Matlab. Just help other people with another, or simply less background.

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 10, 2021, 3:59pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/9 "2021-08-10T15:59:26Z")

</div>

The job of a serializer should be defined well enough. In the example and test results the focus was on type detection.  
But yes, specific background was not given.  
Besides from learning Julia, I intend to use the serializer for inter-process communication (Matlab\<\>Julia).  
The data structure is relatively simple, some arrays or structs with arrays, each 10e8 double.  
Some differences in the type system between Matlab and Julia need to be bridged.  
There is the very good MAT.jl package taking more or less care of this. But relying on .mat files means a canned save command from Matlab, and the saving time is already dominant… Since there is no fopen, a pipe cannot be used.

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [August 10, 2021, 4:31pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/10 "2021-08-10T16:31:12Z")

</div>

If the goal is to have a way to create an object in Julia and write it to a MATLAB `struct`, can I suggest `Dict`?

---

<div class="post-metadata">

**Author:** ![Vasily\_Pisarev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vasily_pisarev/32/7929_2.png) [@Vasily\_Pisarev](https://discourse.julialang.org/u/Vasily_Pisarev)\
**Post date:** [August 10, 2021, 4:37pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/11 "2021-08-10T16:37:35Z")

</div>

I think `isstruct` may be written more concisely as

```julia
isstruct1(obj::T) where {T} = !isprimitivetype(T)

isstruct1(obj::Union{Array,Tuple,String}) = false

```

Also has benefit of extensibility if you decide add custom dispatch rules for other types.

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 10, 2021, 4:40pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/12 "2021-08-10T16:40:58Z")

</div>

I can only guess, but have no idea how this can help in efficiently serializing and storing/streaming data.  
Reading back (look up) would be only 1x. We talk about 50 or so large arrays organized in structs.

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 10, 2021, 4:47pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/13 "2021-08-10T16:47:44Z")

</div>

Thanks a lot!  
Just found nfields(), have to compare. In the past I had some surprising false positives.  
As said, more structs than you think.

---

<div class="post-metadata">

**Author:** ![Vasily\_Pisarev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vasily_pisarev/32/7929_2.png) [@Vasily\_Pisarev](https://discourse.julialang.org/u/Vasily_Pisarev)\
**Post date:** [August 10, 2021, 6:17pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/14 "2021-08-10T18:17:42Z")

</div>

`nfields` must be more efficient than `applicable` as well. The latter has to search the method table in runtime while the result of the former is a compile-time constant. As such, the compiler might infer it and eliminate some branches in the serialization function.

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 10, 2021, 9:24pm UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/15 "2021-08-10T21:24:30Z")

</div>

Probably no other language has such a large bandwidth from highest (Metaprogramming.jl, Symbolics.jl, Modia.jl) to these levels where the programmer can assist the compiler.

Thanks for sharing this background insight, see also [this](https://discourse.julialang.org/t/intermediate-results-dereferencing-cse-when-to-avoid/66024/3) and [this](https://discourse.julialang.org/t/intermediate-results-dereferencing-cse-when-to-avoid/66024/3).

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 11, 2021, 8:40am UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/16 "2021-08-11T08:40:34Z")

</div>

I could replace all

> if typeof(v) == …

by a suitable dispatch function

> fun(v::T) where {T…

remaining what I called “the match of the last resort”.  
nfields() did not cut it in the end because hitting Tuple. Your isstruct version works as expected

```julia
function serial(v)
    if isstruct(v)
        ...
    else
        error("unsuitable type")
    end
end

```

Since isstruct() is evaluated at compile time, looks like we are done.

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [August 23, 2021, 4:28am UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/17 "2021-08-23T04:28:39Z")

</div>

I mean introducing this correspondence

Matlab:

```matlab
struct with fields:
a: 1
b: "lol"

```

\<=\>

Julia:

```julia
Dict{Symbol, Any} with 2 entries:
:a => 1
:b => "lol" 

```

Because the `Dict` is the closest Julia type to Matlab’s `struct`, a one-off container with named elements.

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [August 23, 2021, 5:41am UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/18 "2021-08-23T05:41:29Z")

</div>

Dicts look close on the disp/show level, but their call looks different: s[a] vs. s.a for a named tuple, or?

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [August 23, 2021, 6:37am UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/19 "2021-08-23T06:37:40Z")

</div>

That dissimilarity is much more superficial than the difference between M `struct` and J `struct`. But sure, a named tuple would work too, it all depends on how you want it to be treated in Julia.

---

<div class="post-metadata">

**Author:** ![BambOoxX](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bambooxx/32/22179_2.png) [@BambOoxX](https://discourse.julialang.org/u/BambOoxX)\
**Post date:** [June 8, 2022, 7:29am UTC](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139/20 "2022-06-08T07:29:43Z")

</div>

Just for the recall, I just had a need for such a test, and I found [`isstructtype`](https://docs.julialang.org/en/v1/base/base/#Base.isstructtype) in `Base` which seems dedicated to that (at least in 1.7.1)

[Next page](https://discourse.julialang.org/t/type-sudoku-test-if-object-is-a-struct/66139.md?page=2)
