How to use a vector as a type parameter in Julia - julia

This is similar to my previous question, but a bit more complicated.
Before I was defining a type with an associated integer as a parameter, Intp{p}. Now I would like to define a type using a vector as a parameter.
The following is the closest I can write to what I want:
type Extp{g::Vector{T}}
c::Vector{T}
end
In other words, Extp should be defined with respect to a Vector, g, and I want the contents, c, to be another Vector, whose entries should be the of the same type as the entries of g.
Well, this does not work.
Problem 1: I don't think I can use :: in the type parameter.
Problem 2: I could work around that by making the types of g and c arbitary and just making sure the types in the vectors match up in the constructor. But, even if I completely take everything out and use
type Extp{g}
c
end
it still doesn't seem to like this. When I try to use it the way I want to,
julia> Extp{[1,1,1]}([0,0,1])
ERROR: type: apply_type: in Extp, expected Type{T<:Top}, got Array{Int64,1}
So, does Julia just not like particular Vectors being associated with types? Does what I'm trying to do only work with integers, like in my Intp question?
EDIT: In the documentation I see that type parameters "can be any type at all (or an integer, actually, although here it’s clearly used as a type)." Does that mean that what I'm asking is impossible, and that that only types and integers work for Type parameters? If so, why? (what makes integers special over other types in Julia in this way?)

In Julia 0.4, you can use any "bitstype" as a parameter of a type. However, a vector is not a bitstype, so this is not going to work. The closest analog is to use a tuple: for example, (3.2, 1.5) is a perfectly valid type parameter.
In a sense vectors (or any mutable object) are antithetical to types, which cannot change at runtime.

Here is the relevant quote:
Both abstract and concrete types can be parameterized by other types
and by certain other values (currently integers, symbols, bools, and
tuples thereof).
So, your EDIT is correct. Widening this has come up on the Julia issues page (e.g., #5102 and #6081 were two related issues I found with some discussion), so this may change in the future - I'm guessing not in v0.4 though. It'd have to be an immutable type really to make any sense, so not Vector. I'm not sure I understand your application, but would a Tuple work?

Related

Can I define variable in Julia just like in Fortran

I am new to Julia. Have a quick question.
In Fortran, we can do
implicit none
Therefore whenever we use a variable, we need to define it first. Otherwise it will give error.
In Julia, I want to do the same time thing. I want to define the type of each variable first, like Float64, Int64, etc. So that I wish that Julia no longer need to automatically do type conversion, which may slow down the code.
Because I know if Julia code looks like Fortran it usually runs fast.
So, in short, is there a way in Julia such that, I can force it to only use variables whose types has been defined, and otherwise give me an error message? I just want to be strict.
I just wanted to define all the variables first, then use them. Just like Fortran does.
Thanks!
[Edit]
As suggested by the experts who answers the questions, thank you very much! I know that perhaps in Julia there is no need to manually define each variables one by one. Because for one thing, if I do that, the code will become just like Fortran, and it can be very very long especially if I have many variables.
But if I do not define the type of the each variables, is Julia smart enough to know the type? Will it do some conversions which may slow down the code?
Also, even if there is no need to define variables one by one, is there in some situations we may have to manually define the type manually?
No, this is not possible* as such. You can, however, add type annotations at any point in your code, which will raise an error if the variable is not of the expected type:
julia> i = 1
1
julia> i::Int64
1
julia> i = 1.0
1.0
julia> i::Int64
ERROR: TypeError: in typeassert, expected Int64, got a value of type Float64
Stacktrace:
[1] top-level scope
# REPL[4]:1
julia> i=0x01::UInt8
0x01
*Julia is a dynamically-typed language, though there are packages such as https://github.com/aviatesk/JET.jl for static type-checking (i.e., no type annotations required) and https://github.com/JuliaDebug/Cthulhu.jl for exploring Julia's type inference system.
Strict type declaration is not how you achieve performance in Julia.
You achieve the performance by type stability.
Moreover declaring the types is usually not recommended because it decreases interoperability.
Consider the following function:
f(x::Int) = rand() < 4+x ? 1 : "0"
The types of everything seem to be known. However, this is a terrible (from performance point of view) function because the type of output can not be calcuated by looking types of input. And this is exactly how you write the performant code with regard to types.
So how to check your code? There is a special macro #code_warntype to detect such cases so you can correct your code:
Another type related issue are the containers that should not be specified by abstract elements. So you never want to have neither Vector{Any} nor Vector{Real} - rather than that you want Vector{Int} or Vector{Float64}.
See also https://docs.julialang.org/en/v1/manual/performance-tips/ for further discussion.

How to check if a variable is scalar in julia

I would like to check if a variable is scalar in julia, such as Integer, String, Number, but not AstractArray, Tuple, type, struct, etc. Is there a simple method to do this (i.e. isscalar(x))
The notion of what is, or is not a scalar is under-defined without more context.
Mathematically, a scalar is defined; (Wikipedia)
A scalar is an element of a field which is used to define a vector space.
That is to say, you need to define a vector space, based on a field, before you can determine if something is, or is not a scalar (relative to that vector space.).
For the right vector space, tuples could be a scalar.
Of-course we are not looking for a mathematically rigorous definition.
Just a pragmatic one.
Base it off what Broadcasting considers to be scalar
I suggest that the only meaningful way in which a scalar can be defined in julia, is of the behavior of broadcast.
As of Julia 1:
using Base.Broadcast
isscalar(x::T) where T = isscalar(T)
isscalar(::Type{T}) where T = BroadcastStyle(T) isa Broadcast.DefaultArrayStyle{0}
See the docs for Broadcast.
In julia 0.7, Scalar is the default. So it is basically anything that doesn't have specific broadcasting behavior, i.e. it knocks out things like array and tuples etc.:
using Base.Broadcast
isscalar(x::T) where T = isscalar(T)
isscalar(::Type{T}) where T = BroadcastStyle(T) isa Broadcast.Scalar
In julia 0.6 this is a bit more messy, but similar:
isscalar(x::T) where T = isscalar(T)
isscalar(::Type{T}) where T = Base.Broadcast._containertype(T)===Any
The advantage of using the methods for Broadcast to determine if something is scalar, over using your own methods, is that anyone making a new type that is going to act in a scalar way must make sure it works with those methods correctly
(or actually nonscalar since scalar is the default.)
Structs are not not scalar
That is to say: sometimes structs are scalar and sometimes they are not and it depends on the struct.
Note however that these methods do not consider struct to be non-scalar.
I think you are mistaken in your desire to.
Julia structs are not (necessarily or usually) a collection type.
Consider that: BigInteger, BigFloat, Complex128 etc etc
are all defined using structs
I was tempted to say that having a start method makes a type nonscalar, but that would be incorrect as start(::Number) is defined.
(This has been debated a few times)
For completeness, I am copying Tasos Papastylianou's answer from the comments to here. If all you want to do is distinguish scalars from arrays you can use:
isa(x, Number)
This will output true if x is a Number (like a float or an int), and output false if x is an Array (vector, matrix, etc.)
I found myself needing to capture the notion of if something was scalar or not recently in MultiResolutionIterators.jl.
I found the boardcasting based rules from the other answer,
did not meet my needs.
In particular I wanted to consider strings as nonscalar.
I defined a trait,
bases on method_exists(start, (T,)),
with some exceptions as mentioned e.g. for Number.
abstract type Scalarness end
struct Scalar <: Scalarness end
struct NotScalar <: Scalarness end
isscalar(::Type{Any}) = NotScalar() # if we don't know the type we can't really know if scalar or not
isscalar(::Type{<:AbstractString}) = NotScalar() # We consider strings to be nonscalar
isscalar(::Type{<:Number}) = Scalar() # We consider Numbers to be scalar
isscalar(::Type{Char}) = Scalar() # We consider Sharacter to be scalar
isscalar(::Type{T}) where T = method_exists(start, (T,)) ? NotScalar() : Scalar()
Something similar is also done by AbstractTrees.jl
isscalar(x) == applicable(start, x) && !isa(x, Integer) && !isa(x, Char) && !isa(x, Task)

Subtype with different parameter than supertype

Let's say I have defined a Type merely as a alias for a certain array in Julia but with additional information, let's say just a string
abstract A{T,N}
foo::AbstractArray{T,N}
bar::Real
end
I would like to define a Subtype having maybe another element but also restricting the second parameter of the type to be an integer and having a value of N+1 if B is of type N.
type B{N::Int} <: A{T,N+1}
baz::Float64
end
In this example neither ::Int nor N+1 seem to be right nor are they syntactically. I'm a little new to Julia but I read a lot the last days and couldn't find a solution for that.
How can I do such a “restricted” subtype?
Edit: Maybe there's even another glitch. For the supertype N should be able to be a vector specifying the size of foo while for the subtype it should be an integer specifying the length of the vector.
Edit 2: I meant to use an abstract A which I edited now as mentioned in the comments
Edit 3: So I think one problem seems to be, that abstract types can not have fields (which I don't understand why but anyhow), then still I can't declare Type parameters to be e.g. just an Integer.
So how can I do something like
abstract A{T,N}
type B{N::Integer} <: A{Float64,N+1}
v:FixedVector{N+1,Float64}
end
I always get the problem, that N always (no matter what I do) stays a Typevar while I would like to just have an Integer. So is there a way to make a type dependent on a variable?

Function of parameter type in type definition

Assume I want to store I vector together with its norm. I expected the corresponding type definition to be straightforward:
immutable VectorWithNorm1{Vec <: AbstractVector}
vec::Vec
norm::eltype(Vec)
end
However, this doesn't work as intended:
julia> fieldtype(VectorWithNorm1{Vector{Float64}},:norm)
Any
It seems I have to do
immutable VectorWithNorm2{Vec <: AbstractVector, Eltype}
vec::Vec
norm::Eltype
end
and rely on the user to not abuse the Eltype parameter. Is this correct?
PS: This is just a made-up example to illustrate the problem. It is not the actual problem I'm facing.
Any calculations on a type parameter currently do not work
(although I did discuss the issue with Jeff Bezanson at JuliaCon, and he seemed amenable to fixing it).
The problem currently is that the expression for the type of norm gets evaluated when the parameterized type is defined, and gets called with a TypeVar, but it is not yet bound to a value, which is what you really need it to be called with, at the time that that parameter is actually bound to create a concrete type.
I've run into this a lot, where I want to do some calculation on the number of bits of a floating point type, i.e. to calculate and use the number of UInts needed to store a fp value of a particular precision, and use an NTuple{N,UInt} to hold the mantissa.

How do I create a synonym for a type class name?

I want to abbreviate create a synonym for a type class name. Here's how I'm doing it now:
class fooC = linordered_idom
instance int :: fooC
proof qed
definition foof :: "'a::fooC ⇒ 'a" where
"foof x = x"
term "foof (x::int)"
value "foof (x::int)"
This works fine if there's not a better way to do it. The disadvantage is that I have to instantiate int, and the class command takes time to implement itself.
Update 140314
This update is to clarify for Makarius what it is I want, to explain my purpose in wanting it, and give a list of commands that I'm familiar with for creating notation, abbreviations, and synonyms, but commands which I couldn't get to work for what I want.
My initial choice of "abbreviation" rather than "synonym"
I guess "synonym" would have been a better word, but I chose "abbreviation" because it describes what I want, which is to be able to create a shorter name for for a type class, like renaming linordered_semidom to losdC. Though Isar abbreviation has some of the attributes of definition, it also just defines syntax. So, because "abbreviate" describes what I want, and abbreviation just defines syntax, I chose "abbreviation" instead of "synonym" or "alias".
Synonym/alias, Isar commands I couldn't get to work for that
"Alias" would describe what I want. As to the sentence "If you just want to save typing in the editor, you could use some abbreviations there," here are the commands I've experimented with to try and rename linordered_idom, but I couldn't get them to work for me:
type_notation
type_synonym
notation
abbreviation
syntax
Rather than explain what I've tried, and try to remember what I tried, I just list them. I did searches on "class" and only found the Isar commands class and classes. I thought maybe locale commands might be applicable, but I didn't find anything.
What I want is simple, like how type_synonym is used to define synonyms for types.
The purpose
There is my general desire to shorten type class names such as linordered_idom, because eventually, I plan on using the algebra type classes extensively.
However, there is a second reason, and that is to rename something like linordered_semidom to be part of a naming scheme of three types.
For any algebraic type class, such as linordered_semidom, I can use that type class, along with quotient_type, to create what I'll call a number system, such as how nat is used to define int.
Using Int.thy as a template, I did that with linordered_semidom, and then instantiated it as comm_ring_1, which is as far as I have time to go these days.
Additionally, with typedef, for any algebraic type class which has the dependencies of zero and one (and others such as ord), I can define a type of all elements greater than or equal to zero, and another one for all elements greater than zero. I did that for linordered_idom, but then I figured out that I actually needed to go the quotient_type route, to get things that model rat.
That's the long explanation. Eventually, I'll start working with numerous algebraic type classes, and from one type class, I'll get two more. If I do that for 20 type classes, and also use them, then long, descriptive names don't work, and renaming type classes will help me in knowing what type classes go together.
Here would be the scheme for linordered_semidom, where I don't know how this will actually work out, until I'm able to try it all:
linordered_semidom is the base class. I rename it to losdC. It's the numbers greater than or equal to zero for these three types.
losdQ is defined from losdC using quotient_type. It gives me the negative numbers, and the ability to coerce losdC to losdQ.
losd1 is defined using typedef, and is the numbers greater than zero.
I need a consistent naming scheme, to keep it all straight: losdC, losdQ, and losd1.
Finally, eventually even 4 types instead of 3 types
I haven't completely worked and thought things out (I'm not even close), but analogously, it's all related to implementing, for algebra type classes, the basic relationship between nat, int, and rat, where real might eventually come into play. Additionally, it's about getting a type, from these types, of the non-negative or positive members, if those don't come by default.
There is nat used for int, and int used for rat.
With nat used for int, we get the non-negative integers by default, which is nat.
With int used for rat, we don't get the non-negative members of rat, we get fractions. (Again, I'm talking about a type of non-negatives and positives, not a set of non-negatives and positives.)
So, if I use linordered_idom and quotient_type to define fractions, then I have to use typedef twice to get the non-negative and positive members of those fractions, which means I would have 4 types to keep track of, liodC, liodQ, liod0, and liod1.
If there's a simple solution to renaming type classes, then I've unnecessarily said about 600 words.
A definition is not an abbreviation, it introduces a separate term that is logical equal. That works for term constants.
A type class is semantically a predicate over types, and thus connected to some predicate (term constant), but in practice you rarely access that.
So what exactly means to "abbreviate a type class"?
For example, you might want to manipulate the class name space to get an alias for it, which is in principle possible. But what is the purpose?
If you just want to save typing in the editor, you could use some abbreviations there.
Another possibility, within the formal system, is to introduce genuine aliases in the name space. Isabelle provides some facilities for that, which are not very much advertized, because there is a real danger of obscuring libraries and preventing anyone else from understanding them, if names are changed too much.
This is how it works, using some friendly Isabelle/ML within the theory source:
class foobar = ord + fixes foobar :: 'a
setup {* Sign.class_alias #{binding f} #{class foobar} *}
typ "'a::f"
instantiation nat :: f
begin
definition foobar_nat :: nat where "foobar_nat = 0"
instance ..
end
Note that Sign.class_alias only refers to the type class name space in the narrow sense. A class is many things at the same time: locale, const (the prodicate), type class. You can see this in the following examples where the class is used as "target" for local definitions and theorems:
definition (in foobar) "fuzz = foobar"
theorem (in foobar) "fuzz = foobar" by (simp add: fuzz_def)
Technically, the locale name space used above could support aliases as well, but this is not done. Only basic Sign.class_alias, Sign.type_alias, Sign.const_alias are exposed for unusual situations, to address problems with legacy libraries.

Resources