Julia: Non-type as type parameters

Created on 3 Jan 2015  Â·  16Comments  Â·  Source: JuliaLang/julia

When a type parameter is specified to be T <: Top, it actually accept non-type as parameters as well.

julia> type A{T<:Top}
       end

julia> A{1}
A{1}

julia> A{1.2}
A{1.2}

Although 1 and 1.2 are clearly not subtypes of Top

julia> 1 <: Top
ERROR: type: subtype: expected Type{T<:Top}, got Int64

julia> 1.2 <: Top
ERROR: type: subtype: expected Type{T<:Top}, got Float64

This is also the case for the builtin Type (some other types like Array doesn't even have this constraint specified)

julia> Type
Type{T<:Top}

julia> Type{1}
Type{1}

julia> Type{1.2}
Type{1.2}

No error is raised even if this parameter is used as a type of a field

julia> type A{T<:Top}
       a::T
       end

julia> A{1}
A{1}

julia> A{1}.names
(:a,)

julia> A{1}.types
(1,)

The same thing happens for T <: Any although this time it correctly reject Core.Unref

julia> type A{T <: Any}
       end

julia> A{1}
A{1}

julia> A{1.2}
A{1.2}

julia> A{Core.Undef}
ERROR: type: A: in T, expected T, got Type{Undef}

julia> Core.Undef <: Any
false

Specifying other types seems fine

julia> type C{T <: Integer}
       end

julia> C{1}
ERROR: type: C: in T, expected T<:Integer, got Int64

julia> C{Int}
C{Int64}

Another related issue is that Vararg doesn't specify any constraint on the parameter and therefore the following syntax is allowed.

julia> (1...)[1]
1...

julia> typeof((1...)[1])
DataType
types and dispatch

Most helpful comment

At some point I was also thinking that it might be nice to be able to write sth like

type A{T<:Top, N::Int}
...
end

So that the first parameter has to be a type and the second has to be a Int.

The (only) benefit would be less confusion and less manual checking if one want to be safe but I'm not sure how big a change it requires in the underlying system...

Edit: I am aware that T <: Top is a boolean expression while N::Int is an assertion so it's not that consistent but I guess the meaning should be pretty clear....

All 16 comments

This is not a high priority, since it doesn't prevent anybody from getting work done.

I agree, I couldn't really think of a situation that this can affect the correctness of valid code either. It's only a little bit confusing when I did sth wrong and the error pops up in unrelated place etc...

the Top and Undef types are gone now. its unclear to me if type A{T<:Any} end is supposed to mean something different from type A{T} end, which was the remaining odd case observed here.

Well it would definitely seem reasonable that T would be constrained to be
a type in the former case, but I don't know enough about the internals to
day whether it would be reasonable to change it to work like that.

Yeah, I relized that Top and Undef are gone. However, this issue is about T <: Any (and also T <: Top before) accepting non-type as type parameters.

Conceptually type A{T<:Any} end means A{1} is not allowed.

I guess it requires a field in TypeVar to define whether non-type is allowed? Although I guess there's a number of other places that needs to be changes as well.

At some point I was also thinking that it might be nice to be able to write sth like

type A{T<:Top, N::Int}
...
end

So that the first parameter has to be a type and the second has to be a Int.

The (only) benefit would be less confusion and less manual checking if one want to be safe but I'm not sure how big a change it requires in the underlying system...

Edit: I am aware that T <: Top is a boolean expression while N::Int is an assertion so it's not that consistent but I guess the meaning should be pretty clear....

This idea has come up before (a long time ago I believe), I also think that
it would be a good addition (and don't know how hard it would be).

As far as I can tell, the Julia documentation does at the moment not explicitly state, what values are permitted as type parameters. All the examples I found show only other types as type parameters. Are e.g. integer values also allowed as type parameters? Any immutable value that can be created at compiler time (e.g. a text string)?

A good practical application example of an integer parameter would be a parametric modular-arithmetic type, where the modulus is compiled into the code, rather than being encoded as a run-time parameter along with each value. Imagine an application that does a lot of modular arithmetic, but only ever with the three fixed modulus values 5, 7 and 17. It would be great to be able to implement that via a single parametric "type Mod{modulus::Int} v::Int; end", which the programmer can then instantiate as types Mod{5}, Mod{7} and Mod{17} and add with "+(x::Mod{N}, y::Mod{N}) = Mod{N}(mod(x.v+y.v, N))"?

At the moment, the latter results in "ERROR: UndefVarError: N not defined".

Please ask questions about usage on our discourse message board.

For documentation, see the first section of the page you linked, the last bullet point. Integers as type parameters are fully supported and frequently used. That's how we encode the dimensionality in our arrays. In fact, your exact example is in the examples/ folder.

The thing you're running into is that Julia does not support isa (::) restrictions within type parameter lists. Only subtype (<:) expressions are currently supported. But this is unrelated to this issue. If you have further questions, please continue the discussion on the message board.

@yuyichao Could another benefit of the N::Int syntax be expressions like:

const PlusTwoD{T,N::Int} = Array{T,N+2}

I'm not sure that deserves its own feature request separate from this, but it is a feature I would find useful for writing more readable code. One simple example would be for making space-time arrays, since there's always a single implicit time dimension but any number of space dimensions, and I want to have SpaceTime{SpaceDimensionality} correspond directly to a timeseries of Space{SpaceDimensionality}.

edit: Though then what would PlusTwoD{Int,3}.parameters return? Maybe this is a separate (if dependent) request?

The implementable subset of it is https://github.com/JuliaLang/julia/issues/18466

What was the ultimate outcome here? Are non-type parameters still under consideration or not?

Non-type parameters are fully supported and always will be. They simply must be isbits. See the fourth bullet point in the documentation for types.

This issue is about "abusing" the syntax T<:Any in type parameters — that currently means that they're completely unrestricted and also allow non-type values, but ideally it'd require a subtype relation like it does everywhere else.

Ok, the way I ended up here was that I was trying to parametize a struct based on a UInt8 value, something like this:

julia> mutable struct NDimArray{N::UInt8}
    arry::Array{Int64, N}
end
ERROR: syntax: invalid variable expression in "where"

(I guess I was taking a bit too much inspiration from C++ non-type template params where you have to specify the type)

Just now I tried not specifying the type of N:

julia> mutable struct NDimArray{N}
          arry::Array{Int64, N}
       end

That seemed to work, though it doesn't convey the constraint that N should be a unsigned integer of some sort.

Also, can that non-type parameter 'N' be used in an inner constructor inside the struct definition?

EDIT: yes that seems to be possible:

julia> mutable struct NDimArray{N}
          arry::Array{Int64, N}

          NDimArray{N}(i::Integer) where {N} = new([N*i])
       end

but the 'where' seems a bit odd there.

Right, we currently don't support :: constraints within type parameters, but you can use non-type parameters everywhere — including in inner constructors that can enforce your constraints at construction time. Further questions here would probably be better suited for the discourse mailing list.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

m-j-w picture m-j-w  Â·  3Comments

dpsanders picture dpsanders  Â·  3Comments

arshpreetsingh picture arshpreetsingh  Â·  3Comments

tkoolen picture tkoolen  Â·  3Comments

wilburtownsend picture wilburtownsend  Â·  3Comments