How to change parametric type during instantiation in Julia

213 Views Asked by At

I'm trying to do the following

type Foo{T}
  x::changeType(T)
end

where the function changeType changes the type parameter to some other type. It doesn't have to be a function, I'm happy using a dictionary or a macro or w/e, I just need a way to do this.

I tried it with both a function and a dictionary and both resulted in errors.

1

There are 1 best solutions below

2
On

I believe this is not entirely possible. However, you can play with a couple of workarounds.

  1. Do not constrain the type of x and just instantiate x with the proper type manualy:

    type Foo{T}
        x
        y::T
    end
    
    >>> f = Foo{Int32}(5.0f0, 2)
    Foo{Int32}(5.0f0,2)
    >>> typeof(f.x), typeof(f.y)
    (Float32, Int32)
    
  2. You can wrap your object in a function:

    const types = Dict(Int64 => Float32)
    
    type Foo{T}
        x::T
    end
    
    foo(k) = Foo{get(types, T, T)}
    

    Then create an object of Foo

    >>> foo(Int64)
    Foo{Float32}
    
  3. If you want to have mixed type fields in the same type (e.g. fields of T and map(T)) you can modify a bit the constructor:

    const types = Dict(Int64 => Float32)
    
    type Foo{T}
        x
        y::T
    
        Foo(x=0, y=0) = new(get(types, T, T)(x), y)
    end
    

    This will allow you to create Foo as Foo{Int64} while mapping x to Float32:

    >>> Foo{Int64}(5, 2)
    Foo{Int64}(5.0f0, 2)     # x is Float32, y is Int64
    
  4. And the last, and probably the most viable one: first define the dictionary and wrap your type in both types:

    const types = Dict(Int64 => Float32)
    
    type Foo{T, V}
        x::V
        y::T
    end
    

    Now wrap the construction of a Foo object into a function:

    foo(T) = Foo{T, get(types, T, T)}
    foo(T, args...) = Foo{T, get(types, T, T)}(args...)
    

    foo function creates objects of type Foo where the first parameter specifies the type T of Foo and the type V is dynamically inferred from the types dictionary.

    >>> foo(Int64)
    Foo{Int64,Float32}
    >>> foo(Int64, 5, 2)
    Foo{Int64,Float32}(5.0f0,2) # x is Float32, y is Int64
    

Note: in both the above methods, if T is not defined in the types dictionary, the get function returns T and thus x is mapped to T. Is a fall-back method for types that don't require mapping. E.g. for the third option:

>>> Foo{Int32}(5, 2)
Foo{Int32}(5,2)

both x and y are Int32 since Int32 is not in the mapping dict types. And for the fourth option:

>>> foo(Int32)
Foo{Int32,Int32}

I think currently the type of x cannot be specified at compile time as a function of T, but the above workarounds should do the job.

I don't know neither how smart is the Julia compiler.. given that the types dictionary is constant, it might do some smart things and infer the type of x from there (maybe a dev could answer this or give further improvements).