I'm interested in defining a struct that has a field which is a vector of vectors. Potentially (but not necessarily), the inner vectors would be of type SVector (defined in the StaticArrays package). My naive approach would be to declare the field x::AbstractVector{AbstractVector{T}}; however, julia doesn't regard, say, Vector{SVector{3, Float64}} to be an instance of AbstractVector{AbstractVector}. For that matter, it doesn't regard Vector{Vector{64}} to be AbstractVector{AbstractVector} either. It seems as though the contained type has to be a concrete type, or left out entirely. Am I going about this in the wrong way?
Use AbstractVector{<:AbstractVector} as this is a construct accepting any vector whose element type is a subtype of AbstractVector.
Instead AbstractVector{AbstractVector} requires element type to be AbstractVector exactly.
Using structs with abstract fields are not generally recommended, especially for performance reasons. In a struct field AbstractVector{<:AbstractVector} is abstract, even if the eltype is concrete.
Using AbstractVector{<:AbstractVector} in a function signature is fine, but probably not in a struct definition. Try this instead:
struct Foo{T<:AbstractVector{<:AbstractVector}}
x::T
end
This will give you a concretely typed field, where the type of the field x is encoded in the type parameter.
Related
I have been trying to understand the type system for Julialang but some design aspects are still confusing me. I was hoping someone could clarify.
So the question here is about Abstract Types and their concrete implementations. From what I understand Julia Abstract types do not enforce any constraints on their concrete implementations. So there is no guarantee that a method that works on the Abstract type will work on a concrete implementation of that type.
I get that Julia does not use classes or follow inheritance. But I just want to avoid generating all sorts of bugs in my code. If there is a different design paradigm, then could someone please answer question 2 below.
So I have 2 questions.
Is this still the way that the language works? Just to confirm nothing has changed since the blog post.
How do users design their software around this seeming vulnerability?
And example of the issue from the linked post:
abstract type AbstractPerson end
abstract type AbstractStudent <: AbstractPerson end
abstract type AbstractTeacher <: AbstractPerson end
struct Person <: AbstractPerson
name::String
end
struct Student <: AbstractStudent
name::String
grade::Int
hobby::String
end
struct MusicStudent <: AbstractStudent
grade::Int
end
Now if I create some methods on the abstract type.
get_name(x::AbstractPerson) = x.name
p1 = Person("elroy")
get_name(p1)
>"elroy"
So even if MusicStudent is a subtype of AbstractPerson, the MusicStudent DOES NOT have a name attribute. That means that to following behavior is observed.
m1 = MusicStudent(10)
get_name(m1)
ERROR: type MusicStudent has no field name
Stacktrace:
[1] getproperty(::Any, ::Symbol) at ./sysimg.jl:18
[2] get_name(::MusicStudent) at ./In[2]:1
[3] top-level scope at In[13]:2
So the problem here is that Julia allows me to instantiate the type variable m1 with essentially an incomplete constructor. And it only gives me an error when I try to run that function.
So that means if I write a function for the Abstract Type, I can't guarantee every concrete implementation of that type has the same interface. That seems like it will make very fragile code as the developer won't know which types implement which attributes and methods.
Isn't this kind of behavior just a bug in the implementation of the Persons? If you really want the behavior to go without exception you can define a default method:
julia> get_name(p::AbstractPerson) = try return p.name catch y return "" end
get_name (generic function with 1 method)
julia> m1 = MusicStudent(10)
MusicStudent(10)
julia> get_name(m1)
""
I think the underlying struggle may be that in Julia you cannot inherit the data field called "name" as part of the object hierarchy. There is a nice discussion of that genuine issue here (see the mention of the #forward macro):
https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231
The basic answer is that in julia the interface of a method is thought of as the methods that are defined to take an element of that type. AbstractArray for example specifies that implementations should implement getIndex and size. The reason to not make fields part of the interface is that not doing so allows for memory efficient code, since each type can define the methods in the most sensible way. For example if I want to make a type called Bob that is a subtype for all people named bob, I don't want to store his name every time. by using methods, Julia allows much more potential for future expansion in unexpected ways.
Technically this approach loses "safety", but the only way it does is if you write code using fields that might not exist, in which case you will get an error. This type of safety isn't that useful since it just gives you a compile error that slows down development.
Is there any way to get all implementations of some abstract type? Like:
implementations(AbstractString) == [String, DirectIndexString, ...]
Would be really handy. Currently I just register all implementations by hand when I need this functionality.
I think this is what you mean
julia> subtypes(AbstractString)
6-element Array{Union{DataType, UnionAll},1}:
Base.SubstitutionString
Base.Test.GenericString
DirectIndexString
RevString
String
SubString
Equally, the reverse of this is supertype, though, by contrast, if you want to travel up the tree you'll have to do that in steps. Then again, subtypes also just gives you subtypes one level down, and you can still use it recursively to travel down the type tree.
If what you mean instead is to find only concrete implementations that are subtypes of that type, you can go through all subtypes recursively until you reach bottom, and you can then further also use isleaftype to test if they are concrete types on top of that.
Note: A parametric type may return false with isleaftype even though it has no subtypes under it. e.g. isleaftype(SubString) returns false, but isleaftype(Substring{String}) returns true.
In order to practising with Julia, I am implementing a little module containing some fixed step ODE solvers (Euler, Runge Kutta, Bulirsch Stoer) using the iterator interface.
My idea was to use multiple dispatch to apply the correct method of the function next to the particular iterator, however the Euler and Runge Kutta iterator type (actually immutable) old the same data.
So I have to choose between:
create two immutable type identical except for the name or
crate a unique immutable with an additional field (say solving_method) and use branching instead of multiple dispatch to address this issue
Both choices seem clunky to me (in particular the second, because the solving_method field is checked at every iteration).
Reading the online discussions about inheritance in Julia I understood that Julia does not have (and will never have) subtypes of concrete types, meaning that one cannot "add fields" to a parent type in this way.
But why I cannot have subtypes of concrete types just for dispatching purposes?
One idiomatic way to solve this flavor of problem is to create a type that stores parameters or the state of the solver and then have a second immutable to specify the method:
type SolverOptions
# ... step size, error tol, etc.
end
immutable RungeKutta end
immutable Euler end
function solve(problem::ODE, method::RungeKutta, options::SolverOptions)
# ... code here ...
end
function solve(problem::ODE, method::Euler, options::SolverOptions)
# ... code here ...
end
Of course, RungeKutta and Euler need not be empty if you want to store some data in there. This isn't always the best solution (and I can't be sure that it will work in your particular case) but it can help when you are trying to prevent duplication of fieldnames.
Maybe try parametric types?
abstract OdeType
abstract Euler <: OdeType
abstract RK4 <: OdeType
immutable Common{T<:OdeType}
x::Int
end
Ceylon has several different concepts for things that might all be considered some kind of array: List, Tuple, Sequence, Sequential, Iterable, Array, Collection, Category, etc. What's is different about these these types and when should I use them?
The best place to start to learn about these things at a basic level is the Ceylon tour. And the place to learn about these things in depth is the module API. It can also be helpful to look at the source files for these.
Like all good modern programming languages, the first few interfaces are super abstract. They are built around one formal member and provide their functionality through a bunch of default and actual members. (In programming languages created before Java 8, you may have heard these called "traits" to distinguish them from traditional interfaces which have only formal members and no functionality.)
Category
Let's start by talking about the interface Category. It represents types of which you can ask "does this collection contain this object", but you may not necessarily be able to get any of the members out of the collection. It's formal member is:
shared formal Boolean contains(Element element)
An example might be the set of all the factors of a large number—you can efficiently test if any integer is a factor, but not efficiently get all the factors.
Iterable
A subtype of Category is the interface Iterable. It represents types from which you can get each element one at a time, but not necessarily index the elements. The elements may not have a well-defined order. The elements may not even exist yet but are generated on the fly. The collection may even be infinitely long! It's formal member is:
shared formal Iterator<Element> iterator()
An example would be a stream of characters like standard out. Another example would be a range of integers provided to a for loop, for which it is more memory efficient to generate the numbers one at a time.
This is a special type in Ceylon and can be abbreviated {Element*} or {Element+} depending on if the iterable might be empty or is definitely not empty, respectively.
Collection
One of Iterable's subtypes is the interface Collection. It has one formal member:
shared formal Collection<Element> clone()
But that doesn't really matter. The important thing that defines a Collection is this line in the documentation:
All Collections are required to support a well-defined notion of value
equality, but the definition of equality depends upon the kind of
collection.
Basically, a Collection is a collection who structure is well-defined enough to be equatable to each other and clonable. This requirement for a well-defined structure means that this is the last of the super abstract interfaces, and the rest are going to look like more familiar collections.
List
One of Collection's subtypes is the interface List. It represents a collection whose elements we can get by index (myList[42]). Use this type when your function requires an array to get things out of, but doesn't care if it is mutable or immutable. It has a few formal methods, but the important one comes from its other supertype Correspondence:
shared formal Item? get(Integer key)
Sequential, Sequence, Empty
The most important of List's subtypes is the interface Sequential. It represents an immutable List. Ceylon loves this type and builds a lot of syntax around it. It is known as [Element*] and Element[]. It has exactly two subtypes:
Empty (aka []), which represents empty collections
Sequence (aka [Element+]), which represents nonempty collections.
Because the collections are immutable, there are lots of things you can do with them that you can't do with mutable collections. For one, numerous operations can fail with null on empty lists, like reduce and first, but if you first test that the type is Sequence then you can guarantee these operations will always succeed because the collection can't become empty later (they're immutable after all).
Tuple
A very special subtype of Sequence is Tuple, the first true class listed here. Unlike Sequence, where all the elements are constrained to one type Element, a Tuple has a type for each element. It gets special syntax in Ceylon, where [String, Integer, String] is an immutable list of exactly three elements with exactly those types in exactly that order.
Array
Another subtype of List is Array, also a true class. This is the familiar Java array, a mutable fixed-size list of elements.
drhagen has already answered the first part of your question very well, so I’m just going to say a bit on the second part: when do you use which type?
In general: when writing a function, make it accept the most general type that supports the operations you need. So far, so obvious.
Category is very abstract and rarely useful.
Iterable should be used if you expect some stream of elements which you’re just going to iterate over (or use stream operations like filter, map, etc.).
Another thing to consider about Iterable is that it has some extra syntax sugar in named arguments:
void printAll({Anything*} things, String prefix = "") {
for (thing in things) {
print(prefix + (thing?.string else "<null>"));
}
}
printAll { "a", "b", "c" };
printAll { prefix = "X"; "a", "b", "c" };
Try online
Any parameter of type Iterable can be supplied as a list of comma-separated arguments at the end of a named argument list. That is,
printAll { "a", "b", "c" };
is equivalent to
printAll { things = { "a", "b", "c" }; };
This allows you to craft DSL-style expressions; the tour has some nice examples.
Collection is, like Correspondence, fairly abstract and in my experience rarely used directly.
List sounds like it should be a often used type, but actually I don’t recall using it a lot. I’m not sure why. I seem to skip over it and declare my parameters as either Iterable or Sequential.
Sequential and Sequence are when you want an immutable, fixed-length list. It also has some syntax sugar: variadic methods like void foo(String* bar) are a shortcut for a Sequential or Sequence parameter. Sequential also allows you to do use the nonempty operator, which often works out nicely in combination with first and rest:
String commaSeparated(String[] strings) {
if (nonempty strings) {
value sb = StringBuilder();
sb.append(strings.first); // known to exist
for (string in strings.rest) { // skip the first one
sb.append(", ").append(string);
// we don’t need a separate boolean to track if we need the comma or not :)
}
return sb.string;
} else {
return "";
}
}
Try online
I usually use Sequential and Sequence when I’m going to iterate over a stream several times (which could be expensive for a generic Iterable), though List might be the better interface for that.
Tuple should never be used as Tuple (except in the rare case where you’re abstracting over them), but with the [X, Y, Z] syntax sugar it’s often useful. You can often refine a Sequential member to a Tuple in a subclass, e. g. the superclass has a <String|Integer>[] elements which in one subclass is known to be a [String, Integer] elements.
Array I’ve never used as a parameter type, only rarely as a class to instantiate and use.
How do I take the address of a value inside an interface?
I have an struct stored in an interface, in a list.List element:
import "container/list"
type retry struct{}
p := &el.Value.(retry)
But I get this:
cannot take the address of el.Value.(retry)
What's going on? Since the struct is stored in the interface, why can't I get a pointer to it?
To understand why this isn't possible, it is helpful to think about what an interface variable actually is. An interface value takes up two words, with the first describing the type of the contained value, and the second either (a) holding the contained value (if it fits within the word) or (b) a pointer to storage for the value (if the value does not fit within a word).
The important things to note are that (1) the contained value belongs to the interface variable, and (2) the storage for that value may be reused when a new value is assigned to the variable. Knowing that, consider the following code:
var v interface{}
v = int(42)
p := GetPointerToInterfaceValue(&v) // a pointer to an integer holding 42
v = &SomeStruct{...}
Now the storage for the integer has been reused to hold a pointer, and *p is now an integer representation of that pointer. You can see how this has the capacity to break the type system, so Go doesn't provide a way to do this (outside of using the unsafe package).
If you need a pointer to the structs you're storing in a list, then one option would be to store pointers to the structs in the list rather than struct values directly. Alternatively, you could pass *list.Element values as references to the contained structures.
A type assertion is an expression that results in two values. Taking the address in this case would be ambiguous.
p, ok := el.Value.(retry)
if ok {
// type assertion successful
// now we can take the address
q := &p
}
From the comments:
Note that this is a pointer to a copy of the value rather than a pointer to the value itself.
— James Henstridge
The solution to the problem is therefore simple; store a pointer in the interface, not a value.
Get pointer to interface value?
Is there a way, given a variable of interface type, of getting a
pointer to the value stored in the variable?
It is not possible.
Rob Pike
Interface values are not necessarily addressable. For example,
package main
import "fmt"
func main() {
var i interface{}
i = 42
// cannot take the address of i.(int)
j := &i.(int)
fmt.Println(i, j)
}
Address operators
For an operand x of type T, the address operation &x generates a
pointer of type *T to x. The operand must be addressable, that is,
either a variable, pointer indirection, or slice indexing operation;
or a field selector of an addressable struct operand; or an array
indexing operation of an addressable array. As an exception to the
addressability requirement, x may also be a composite literal.
References:
Interface types
Type assertions
Go Data Structures: Interfaces
Go Interfaces
In the first approximation: You cannot do that. Even if you could, p itself would the have to have type interface{} and would not be too helpful - you cannot directly dereference it then.
The obligatory question is: What problem are you trying to solve?
And last but not least: Interfaces define behavior not structure. Using the interface's underlying implementing type directly in general breaks the interface contract, although there might be non general legitimate cases for it. But those are already served, for a finite set of statically known types, by the type switch statement.