How to set MIME encoding in Julia? - julia

I am trying to call the following show function which has the signature: function show(io::IO, ::MIME"text/plain", holder::Int). What do I need to do with the 2nd argument such that I can call this function? I am unsure what "::MIME"text/plain" is asking for.

All of
julia> show(stdout, "text/plain", "hi")
"hi"
julia> show(stdout, MIME("text/plain"), "hi")
"hi"
julia> show(stdout, MIME"text/plain"(), "hi")
"hi"
work. The first one is because there's an overload that passes the second argument to the MIME constructor. The two latter ones are just different ways of constructing the mime type:
julia> MIME("text/plain") == MIME"text/plain"()
true
julia> MIME"text/plain"()
MIME type text/plain

Related

Purpose of "where" keyword in constructor of a parametric type

For the Julia manual parametric composite type example
struct Point{T}
x::T
y::T
end
it is possible to write an outer constructor such as
Point(x::T, y::T) where {T} = Point{T}(x, y)
Why is the where {T} part needed, i.e. why isn't
Point(x::T, y::T) = Point{T}(x, y)
possible? Julia complains that T is not defined, but could it not figure out that T is a type from the :: syntax? I am a newcomer to Julia so I might be missing something pretty basic.
T is a variable that has to be defined. If you do not define it in where Julia starts looking for it in an outer scope. Here is an example:
julia> struct Point{T}
x::T
y::T
end
julia> Point(x::T, y::T) = Point{T}(x, y)
ERROR: UndefVarError: T not defined
Stacktrace:
[1] top-level scope at REPL[2]:1
julia> T = Integer
Integer
julia> Point(x::T, y::T) = Point{T}(x, y)
Point
julia> Point(1,1)
Point{Integer}(1, 1)
Note that in this example you do not get what you expected to get, as probably you have hoped for Point{Int64}(1,1).
Now the most tricky part comes:
julia> Point(1.5,1.5)
Point{Float64}(1.5, 1.5)
What is going on you might ask. Well:
julia> methods(Point)
# 2 methods for type constructor:
[1] (::Type{Point})(x::Integer, y::Integer) in Main at REPL[4]:1
[2] (::Type{Point})(x::T, y::T) where T in Main at REPL[1]:2
The point is that The (::Type{Point})(x::T, y::T) where T already got defined by default when the struct as defined so with the second definition you have just added a new method for T = Integer.
In short - always use where to avoid getting surprising results. For example if you would write:
julia> T = String
String
julia> Point(1,1)
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type String
you get a problem. As the signature of Point is fixed when it is defined but in the body T is taken from a global scope, and you have changed it from Integer to String, so you get an error.
Expanding on what #Oscar Smith noted the location of where is also sensitive to scope, and tells Julia on what level the "wildcard" is introduced. For example:
ulia> T1 = Vector{Vector{T} where T<:Real}
Array{Array{T,1} where T<:Real,1}
julia> T2 = Vector{Vector{T}} where T<:Real
Array{Array{T,1},1} where T<:Real
julia> isconcretetype(T1)
true
julia> isconcretetype(T2)
false
julia> T1 isa UnionAll
false
julia> T2 isa UnionAll
true
julia> x = T1()
0-element Array{Array{T,1} where T<:Real,1}
julia> push!(x, [1,2])
1-element Array{Array{T,1} where T<:Real,1}:
[1, 2]
julia> push!(x, [1.0, 2.0])
2-element Array{Array{T,1} where T<:Real,1}:
[1, 2]
[1.0, 2.0]
and you can see that T1 is a concrete type that can have an instance, and we created one calling it x. This concrete type can hold vectors whose element type is <:Real, but their element types do not have to be the same.
On the other hand T2 is a UnionAll, i.e. some of its "widlcards" are free (not known yet). However, the restriction is that in all concrete types that match T2 all vectors must have the same element type, so:
julia> Vector{Vector{Int}} <: T2
true
julia> Vector{Vector{Real}} <: T2
true
but
julia> T1 <: T2
false
In other words in T2 the wildcard must have one concrete value that can be matched by a concrete type.
I'm going to take a different tack from BogumiƂ's excellent answer (which is 100% correct, but might be missing the crux of the confusion).
There's nothing special about the name T. It's just a common convention for a short name for an arbitrary type. Kinda like how we often use the name A for matrices or i in for loops. The fact that you happened to use the same name in the struct Point{T} and in the outer constructor is irrelevant (but handy for the readers of your code). You could have just as well done:
struct Point{SpecializedType}
x::SpecializedType
y::SpecializedType
end
Point(x::Wildcard, y::Wildcard) where {Wildcard <: Any} = Point{Wildcard}(x, y)
That behaves exactly the same as what you wrote. Both of the above syntaxes (the struct and the method) introduce a new name that will behave like a "wildcard" that specializes and matches appropriately. When you don't have a where clause, you're no longer introducing a wildcard. Instead, you're just referencing types that have already been defined. For example:
Point(x::Int, y::Int) = Point{Int}(x, y)
That is, this will reference the Int that was already defined.
I suppose you could say, but if T wasn't defined, why can't Julia just figure out that it should be used as a wildcard. That may be true, but it introduces a little bit of non-locality to the syntax, where the behavior is drastically different depending upon what happens to be defined (or even exported from a used package).

Having some trouble with Julia's conversion rules

In the Julia documentation manual, it says the following [1]:
When is convert called?
The following language constructs call convert:
Assigning to an array converts to the array's element type.
[1] https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#When-is-convert-called?-1
I've defined the following code:
julia> abstract type Element end
julia> abstract type Inline <: Element end
julia> struct Str <: Inline
content::String
end
julia> convert(::Type{Str}, e::String) = Str(e)
convert (generic function with 1 method)
julia> convert(::Type{Element}, e::String) = convert(Str, e)
convert (generic function with 2 methods)
I have convert defined for Julia type String. And converting to Element and converting to Str from an instance of type String works as expected. However, the following fails:
julia> convert(Str, "hi")
Str("hi")
julia> convert(Element, "hi")
Str("hi")
julia> arr = Element[]
0-element Array{Element,1}
julia> push!(arr, "hi")
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Element
Closest candidates are:
convert(::Type{T}, ::T) where T at essentials.jl:168
Stacktrace:
[1] push!(::Array{Element,1}, ::String) at ./array.jl:866
[2] top-level scope at REPL[25]:1
julia> arr = Str[]
0-element Array{Str,1}
julia> push!(arr, "hi")
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Str
Closest candidates are:
convert(::Type{T}, ::T) where T at essentials.jl:168
Str(::String) at REPL[19]:2
Str(::Any) at REPL[19]:2
Stacktrace:
[1] push!(::Array{Str,1}, ::String) at ./array.jl:866
[2] top-level scope at REPL[27]:1
julia>
Can someone explain why the above fails? And how, if it is possible to do so, prevent it from failing?
the main hint of your code was this line:
convert (generic function with 1 method)
that means that the convert function has only one definition (your definition), so something was wrong. when Julia uses convert, it actually calls Base.convert. so, overloading the proper method:
Base.convert(::Type{Str}, e::String) = Str(e)
Base.convert(::Type{Element}, e::String) = convert(Str, e)
worked as expected with your code.
In a nutshell, your code was right, you just were overloading the wrong convert (add always Base.method when working with Julia core functions)

Is it possible to have "public/global" fields in Julia structs?

In Julia it is possible to have public fields in functions for instance
function foo(arg)
global a = arg
a
end
Is it possible to achieve something similar using Julia structures.
For instance what I would like to do is:
julia> struct foobarfoo
global a
end
julia>
julia> test = foobarfoo(1)
ERROR: MethodError: no method matching foobarfoo(::Int64)
Stacktrace:
[1] top-level scope at none:0
julia> a
ERROR: UndefVarError: a not defined
Instead of:
julia> struct foobarfoo
a
end
julia> test = foobarfoo(1)
foobarfoo(1)
julia> test.a
1
julia>
I think that the short answer is no but you may be able to achieve what you want using the #unpack macro of Parameters.jl.

Underspecification when checking complex types in Julia

Given some complex object, e.g.:
> d = Dict(("a", "b")=>3, ("c", "d")=>2)
Dict{Tuple{String,String},Int64} with 2 entries:
("c","d") => 2
("a","b") => 3
I could check the type with:
> isa(d, Dict{Tuple{String, String},Int64})
true
But when I underspecify the Tuple type, it failed the check:
> isa(d, Dict{Tuple,Int64})
false
Is it possible to check for underspecified types in Julia? If so, how? If not, why?
In Julia, dictionaries, like arrays, are invariant. For example, see this question on the concept as it applies to arrays.
This means that:
julia> Int <: Number
true
but,
julia> Vector{Int} <: Vector{Number}
false
and similarly,
julia> Dict{Int, String} <: Dict{Number, String}
false
However, note that Dict on its own is abstract, so
julia> Dict{Int, String} <: Dict
true
and in the code you provide,
julia> isa(d, Dict)
true
As far as I know, if you want to reason specifically about the pair of types in your dictionary, you will need to reference them explicitly. You can do this using keytype(d) and valtype(d). For example, from your question, note that
julia> keytype(d) <: Tuple
true
Of course, if your dictionary is passed into a function, then you can use Dict{T1, T2} in the function signature, and then just reason about T1 and T2...
EDIT: See #FengyangWang's answer for a neat little syntax shortcut being introduced in v0.6
In Julia 0.6 you can simply do
isa(d, Dict{<:Tuple, Int64})
which means a Dict with key type a subtype of Tuple, and value type Int64.
Note that Dict{Tuple, Int64} is not an "underspecified" type: it is a concrete type and can have instances. However, Dict{Tuple{String, String}, Int64} is not the same thing. The former is a type that accepts all tuples as keys, whereas the latter only accepts Tuple{String, String}. This is an example of parametric invariance.

Parametric return types

I'm trying to understand how parametric types work in Julia.
Suppose I have a function foo, which takes an Integer (i.e. non-parametric type), and I want to assert this function to return a vector, whose elements are subtypes of Real. From documentation I deduced that it should be implemented as follows:
function foo{T<:Real}(n::Integer)
# come code to generate variable vec
return vec::Vector{T}
end
But it is not working. What am I doing wrong?
Parametric types are used to parameterise the inputs to a function, not the outputs. If your function is type-stable (read about what that means in the performance tips), then the output of the function can be automatically inferred by the compiler based on the input types, so there should be no need to specify it.
So your function might look something like this:
function foo{T<:Real}(n::T)
#some code to generate vec
return(vec)
end
For example, you can generate a vector of the appropriate type within the body of your function, e.g. something like this:
function f1{T<:Number}(x::T)
y = Array(T, 0)
#some code to fill out y
return(y)
end
But note that T must appear in the argument definitions of the inputs to the function, because this is the purpose of parametric types. You'll get an error if T does not appear in the description of the inputs. Something like this doesn't work:
f{T<:Number}(x::Vector) = x
WARNING: static parameter T does not occur in signature for f at none:1.
The method will not be callable.
f (generic function with 1 method)
But this does, and is type stable, since if the input types are known, then so are the outputs
f{T<:Number}(x::Vector{T}) = x
A parametric method or a constructor of a parametric type
A function is called using the traditional parenthesis syntax:
julia> f(2,3)
5
The above rule is true for both parametric and non-parametric methods.
So, one should not try to call a parametric method like: f{atype}(2,3). I think the source of this mall-usage could be the syntax of calling a parametric type constructor:
julia> typeof(Array{Int})
DataType
julia> Array{Int}(2)
2-element Array{Int32,1}:
57943068
72474848
But in the case of a parametric method:
julia> same_type{T}(x::T, y::T) = true;
julia> typeof(same_type)
Function
julia> same_type{Int}
ERROR: TypeError: Type{...} expression: expected Type{T}, got Function
So with this introduction it has become clear that something like:
function foo{T<:Real}(n::Integer)
.....
end
would be useless, because the value of T won't be determined from caller the arguments.
Make benefit of parametric methods
julia> same_type{T}(x::T, y::T) = true;
julia> same_type(x,y) = false;
julia> same_type(1, 2)
true
julia> same_type(1, 2.0)
false
The main purpose of parametric methods is to let dispatch find the right method to call with respect to argument types when calling a function, but also it has the following idiomatic side effect:
Method type parameters are not restricted to being used as the types
of parameters: they can be used anywhere a value would be in the
signature of the function or body of the function.
julia> mytypeof{T}(x::T) = T
mytypeof (generic function with 1 method)
julia> mytypeof(1)
Int64
Now, let's go back to the main problem of:
How to write a method with Parametric return types?
If your method is already parametric, you could use the parameter value as return type as in the above mytypeof sample or e.g:
julia> newoftype{T}(x::T,n) = T(n)
newoftype (generic function with 1 methods)
julia> newoftype([1,2],4)
4-element Array{Int32,1}:
57943296
72475184
141142104
1970365810
But making return type of a method, a function of arguments value is a straightforward functionality and could be simply done without need to a parametric method. really many of typical methods do this job e.g:
julia> Array(Int,4)
4-element Array{Int32,1}:
126515600
72368848
72474944
0
julia> Array(Float64,4)
4-element Array{Float64,1}:
-2.122e-314
0.0
5.12099e-292
5.81876e-292
It could be done using a argument type of Type e.g:
julia> myarray(T::Type,n::Int)=Array(T,n);

Resources