Remove parameters from struct type

Hi,

Here’s a MWE:

abstract type FooBar end

struct Foo{T} <: FooBar
    a::T
end

struct Bar{T} <: FooBar
   a::T
end

struct Baz
   x
end
struct Baz2 
   x
end

Baz2(b::Baz) = Baz2(b.x)

toBaz2(fb::FooBar) = typeof(fb)(Baz2(fb.a))

toBaz2(Foo(Baz(5)))

EDIT: I made an example that does not rely on cuda

What happens is that this calls Foo{Baz}(::Baz2), which does not work. I would like to know how to obtain only Foo or Bar where there’s currently the typeof function. i.e. to call Foo(::Baz2).

The idea is to define generic functions that applies to any subtype of FooBar. However they throw an error because the type of the a field (T) is part of the struct type, and thus kept in typeof. I also tried to do toBaz2(fb::T) where T <: FooBar, with the same result.

Thanks !

1 Like

toBaz2(fb::FooBar) = Base.typename(typeof(fb)).wrapper(Baz2(fb.a))

does it. A bit obscure for something that does not seem that weird to define.

Speak it for yourself, I could not understand what you want to get from these functions :rofl:

(my comment is just to, perhaps, suggest a more clear description on what you want to achieve and why, maybe it is not clear either to someone that can actually help).

Well in short, I have a set of structures that behave differently but have the same fields. What I want to do is to define a function that moves these fields from cpu to gpu (with CUDA). Instead of defining this function for every struct I do it only for the supertype.

1 Like

I’m not sure what the best approach to your problem is, but here’s a related section of the manual.

If nothing else, you could always just manually implement the various concrete methods that you need for toBaz2, instead of trying to define just one generic method. Generic methods are great, but somewhere along the line there has to be method definitions for concrete types.

@lmiq It looks like the main thrust of the problem is to have a method that converts objects of type Foo{Baz} into objects of type Foo{Baz2} (and similarly for any subtype of FooBar.

1 Like

There might be a better approach, but this is the best I’ve come up with so far:

abstract type FooBar end

struct Foo{T} <: FooBar
    a::T
end

function Foo{T}(f::Foo) where T
    Foo(T(f.a))
end

struct Bar{T} <: FooBar
   a::T
end

function Bar{T}(b::Bar) where T
    Bar(T(b.a))
end

struct Baz
   x
end

struct Baz2
   x
end

Baz2(b::Baz) = Baz2(b.x)

toBaz2(fb::Foo{Baz}) = Foo{Baz2}(fb)
toBaz2(fb::Bar{Baz}) = Bar{Baz2}(fb)

toBaz2(Foo(Baz(5)))
toBaz2(Bar(Baz(5)))

EDIT: Must be careful, though. I’ve introduced the possibility of a stack overflow:

julia> Bar(Bar(3))
ERROR: StackOverflowError:
Stacktrace:
 [1] Bar(::Bar{Int64}) at ./REPL[4]:2
 [2] Bar{Bar{Int64}}(::Bar{Int64}) at ./REPL[5]:2
 ... (the last 2 lines are repeated 39990 more times)
 [79983] Bar(::Bar{Int64}) at ./REPL[4]:2

I need more coffee.

Hopefully somebody will chime in with the right way to do this.

EDIT AGAIN: Being a little more restrictive with my Foo and Bar constructors at least appears to avoid the stack overflow problem:

Foo{Baz2}(f::Foo) = Foo(Baz2(f.a))
Bar{Baz2}(f::Bar) = Bar(Baz2(f.a))
1 Like

What about passing the container type as an argument?

julia> toBaz2(x,T) = T(Baz2(x.a))
toBaz2 (generic function with 1 methods)

julia> julia> x = Foo(Baz(5))

julia> toBaz2(x,Foo)
Foo{Baz2}(Baz2(Baz(5)))

As a side-effect you could change the supertype, who knows that is desirable.

julia> toBaz2(x,Bar)
Bar{Baz2}(Baz2(Baz(5)))