Why can we pass Observation{Int}() in to the brace of DefaultStateStyleEnv{}?
From my understanding, the stuff inside the brace {} should be some kind of type (e.g. DataType) or literal values, but Observation{Int}() is an instance of Observation{Int64}
# they will produce
# ERROR: TypeError: in Array, in element type, expected Type, got a value of type Float64
Array{1.0, 1}();
x=()->1.0; Array{x(), 1}()
So what is happening there?
I am guessing it is something to do with “UnionAll Types” and “Value types” but I couldn’t figure it out.
Thank you in advance.
(There is a 2 links max limit for new users, so I will paste the link to def of DefaultStateStyleEnv as raw text here https://github.com/JuliaReinforcementLearning/ReinforcementLearning.jl/blob/2e1de3e5b6b8224f50b3d11bba7e1d2d72c6ef7c/src/ReinforcementLearningEnvironments/src/environments/wrappers/DefaultStateStyle.jl#L7-L11)
Int aliases Int64 on 64-bit systems and aliases Int32 on 32-bit systems.
julia> Int
Int64
julia> typeof(Int64)
DataType
julia> typeof(Int)
DataType
Any type can be a parameter in the brace, it does not have to be an instance of DataType, e.g. Vector{Union{Int, Nothing}}.
Or are you referring to something else?
Both abstract and concrete types can be parameterized by other types. They can also be parameterized by symbols, by values of any type for which isbits returns true (essentially, things like numbers and bools that are stored like C types or structs with no pointers to other objects), and also by tuples thereof.
Thank you for the help and yes, I notice that, but that is a literal value, which very often treated differently than runtime values in programming languages.
But yes, I don’t fully understand what are the allowed values inside {} in Julia and would be great if you can provide some examples to clarify it. I have read the document you quoted but don’t fully understand it.
I thought only types and literal values are allowed and if you want a instace as type, you need to convert it using the value type Val{x} (provided that it can be converted).
Value-parameters do not need to be literal, but in order to have type stable code, the parameters must be compile-time constants, and of course literal numbers are constant at compile time.
Well, isbits doesn’t cover it all, types, Symbols, and tuples of Symbols and isbits values also work. Actually, the easiest way to check if an instance X is a valid parameter is running Val{X} in the REPL and see if it throws an TypeError.
There’s no special relation, Val{x} is just one of many types with a parameter. Type parameter values, unlike argument values, are known at a method’s compile-time. Since you can put some non-type values in the parameters, that allows for some compile-time calculations in the method.
You can do that with any parametric type, but in cases where you just need to move a value into compile-time and don’t need to create a new type with new features, Val{x} exists for consistency. Otherwise, people would just be creating their own struct MyVal{T} end versions that are incompatible with each other. Val{x}() is just the type’s constructor call that makes its instance, in other words Val{x}() isa Val{x} the same way 1 isa Int.