Skip to content

Commit f60fb83

Browse files
authored
Update docs for hetero types (#149)
1 parent 7cb120a commit f60fb83

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,13 @@ Base.getproperty(b::MyType, s::Symbol) = s == :data ? getfield(b, 1) : getproper
198198
getnamestypes(::Type{NamedTuple{names, types}}) where {names, types} = (names, types)
199199
getnamestypes(::Type{MyType{NT}}) where NT = getnamestypes(NT)
200200

201+
# explicitly give the "schema" of the object to StructArrays
201202
function StructArrays.staticschema(::Type{T}) where {T<:MyType}
202203
names, types = getnamestypes(T)
203204
NamedTuple{(:data, names...), Base.tuple_type_cons(Float64, types)}
204205
end
205206

207+
# generate an instance of MyType type
206208
function StructArrays.createinstance(::Type{T}, x, args...) where {T<:MyType}
207209
names, types = getnamestypes(T)
208210
MyType(x, NamedTuple{names, types}(args))
@@ -303,3 +305,69 @@ julia> s
303305
Foo(44, "d")
304306
Foo(55, "e")
305307
```
308+
309+
In the above example "for structures with non-standard data layout" our `MyType` was composed of `data` of type `Float64` and `rest` of type `NamedTuple`. In many practical cases where there are custom types involved it's hard for StructArrays to automatically widen the types in case they are heterogeneous. The following example demonstrates a widening method in that scenario.
310+
311+
```julia
312+
struct MyType1{T, Names, Types}
313+
data::T
314+
rest::NamedTuple{Names, Types}
315+
end
316+
317+
MyType1(x; kwargs...) = MyType1(x, values(kwargs))
318+
319+
# and a source of custom type data
320+
struct location{U}
321+
x::U
322+
y::U
323+
end
324+
struct region{G<:Array{location}}
325+
area::G
326+
end
327+
328+
function Base.getproperty(x::MyType1, field::Symbol)
329+
if field == :data
330+
getfield(x, :data)
331+
elseif field == :rest
332+
getfield(x, :rest)
333+
else
334+
getproperty(getfield(x, :rest), field)
335+
end
336+
end
337+
338+
getnamestypes(::Type{MyType1{T, Names, Types}}) where {T, Names, Types} = (T, Names, Types)
339+
340+
# explicitly give the "schema" of the object to StructArrays
341+
function StructArrays.staticschema(::Type{T}) where {T<:MyType1}
342+
K, names, types = getnamestypes(T)
343+
NamedTuple{(:data, names...), Base.tuple_type_cons(K, types)}
344+
end
345+
346+
# generate an instance of MyType type
347+
function StructArrays.createinstance(::Type{T}, x, args...) where {T<:MyType1}
348+
K, names, types = getnamestypes(T)
349+
MyType1(x, NamedTuple{names, types}(args))
350+
end
351+
352+
s1 = MyType1(location(1, 0), (place = "Delhi"))
353+
s2 = MyType1(location(2.5, 1.9), (place = "Mumbai"))
354+
s3 = MyType1(region([location(1, 0), location(2.5, 1.9)]), (place = "North India"))
355+
356+
s = [s1, s2, s3]
357+
# Now if we try to do StructArray(s)
358+
# we will get an error
359+
360+
function meta_table(iter)
361+
cols = Tables.columntable(iter)
362+
meta_table(first(cols), Base.tail(cols))
363+
end
364+
365+
function meta_table(data, rest::NamedTuple{names, types}) where {names, types}
366+
F = MyType1{eltype(data), names, StructArrays.eltypes(types)}
367+
return StructArray{F}(; data=data, rest...)
368+
end
369+
370+
meta_table(s)
371+
```
372+
373+
The above example has been tested and implemented in [GeometryBasics.jl](https://github.com/JuliaGeometry/GeometryBasics.jl).

0 commit comments

Comments
 (0)