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
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.
Somewhat embarassed to ask this, but I know it's for the best. I've been programming in Ada for many years now, and understand nearly every part of the language fluently. However, I've never seemed able to wrap my head around T'Class. To borrow from others, can someone "explain it like I'm five?".
Edit: I bought it just to have, but contained within is a great description of, and example use of, T'Class; I refer to “Software Construction and Data Structures with Ada 95” by Michael B. Feldman.
If you start with
package P1 is
type T is tagged private;
procedure Method (Self : T);
end P1;
package P2 is
procedure Proc (Self : T); -- not a primitive
procedure Proc2 (Self : T'Class);
end P2;
In the case of Proc, you are telling the compiler that the parameter should always be considered precisely as of type T (remember that a tagged type is always passed by reference, so the actual type could be derived from T of course, you would not lose the extra data). In particular, that means that within the body of Proc, all calls to Method will be exactly calls to P1.Method, never a call to an overriding Method.
In the case of Proc2, you are telling the compiler that you do not know the exact type statically, so it will need to insert extra code to resolve things at run time. A call to Method, within the body of Proc2, could be call to P1.Method, or to another overriding Method.
Basically: with 'Class, things are resolved at runtime.
Well, if you were five, I would say that T'Class represents the whole family of T.
By family, we mean children and grand-children and grand-grand-children.
As you're not five, it means that this special type represents every tagged type which is in the inheritance tree of T. This way, if you use this type as a parameter, you can use every parameter which has T as ancestor directly or not.
For more information, you can read the wikibooks on this.
I'm a C and Matlab programmer moving to Julia, and I'm trying to understand how function options should look when passed in.
Sometimes, a function provides different functionality based on an argument passed in with a limited number of different options. In C, it could look something like this:
enum Options{
OPTION_1,
OPTION_2
};
// Other arguments omitted
void foo(..., enum Options option){
switch(option){
case OPTION_1:
// Do something
break;
case OPTION_2:
// Do something else
break;
}
}
In Julia, I am not sure how to structure this. My first attempt used a hierarchy of abstract types, and then a function that accepts a singleton type to make the decision. See the following code:
abstract Options
abstract Option_1 <: Options
abstract Option_2 <: Options
function foo{T<:Options}(..., ::Type{T})
if isa(Option_1, Type{T}
//Do something
elseif isa(Option_2, Type{T})
//Do something else
end
end
However, this seems like a very strange way to approach the problem; creating types just to control function input feels awfully strange.
Also, to clarify, I don't think that this is a solution solvable by general parametric methods in Julia. I am looking for the user to be able to specify a flag (such as run version 1 or version 2), not have different functionality based on input type.
Thanks for the help!
I think parametric methods are actually exactly what you are looking for.
abstract Option_1
abstract Option_2
foo{T<:Options_1}(...) = do_something()
foo{T<:Options_2}(...) = do_something_else()
if there is common code between the two implementations then factor it out into another function and use it in both. Julia doesn't have enums but it does have ways to accomplish the same thing and parametric methods are a perfectly valid way to do so.
If you analyse your question, what you are doing is choosing one of two different functionalities via an argument. Thus you actually have two different functions mixed up inside a single function, and you can (and should) split the two different functionalities out into two different functions.
Once you have done this, it's a short step to realise that the way you decide which function to call is probably (or, at least, often) related to the type of an object. But maybe if you give some more details of your actual use case, we can suggest other alternatives. For example, it might just be a boolean variable that turns on or off a certain type of behaviour.
Note that in Julia v0.4 (the current stable version), you can also use #enum to create enums similar to those available in C: see the NEWS.md file.
I am implementing a simple C-like language in OCaml and, as usual, AST is my intermediate code representation. As I will be doing quite some traversals on the tree, I wanted to implement
a visitor pattern to ease the pain. My AST currently follows the semantics of the language:
type expr = Plus of string*expr*expr | Int of int | ...
type command = While of boolexpr*block | Assign of ...
type block = Commands of command list
...
The problem is now that nodes in a tree are of different type. Ideally, I would pass to the
visiting procedure a single function handling a node; the procedure would switch on type of the node and do the work accordingly. Now, I have to pass a function for each node type, which does not seem like a best solution.
It seems to me that I can (1) really go with this approach or (2) have just a single type above. What is the usual way to approach this? Maybe use OO?
Nobody uses the visitor pattern in functional languages -- and that's a good thing. With pattern matching, you can fortunately implement the same logic much more easily and directly just using (mutually) recursive functions.
For example, assume you wanted to write a simple interpreter for your AST:
let rec run_expr = function
| Plus(_, e1, e2) -> run_expr e1 + run_expr e2
| Int(i) -> i
| ...
and run_command = function
| While(e, b) as c -> if run_expr e <> 0 then (run_block b; run_command c)
| Assign ...
and run_block = function
| Commands(cs) = List.iter run_command cs
The visitor pattern will typically only complicate this, especially when the result types are heterogeneous, like here.
It is indeed possible to define a class with one visiting method per type of the AST (which by default does nothing) and have your visiting functions taking an instance of this class as a parameter. In fact, such a mechanism is used in the OCaml world, albeit not that often.
In particular, the CIL library has a visitor class
(see https://github.com/kerneis/cil/blob/develop/src/cil.mli#L1816 for the interface). Note that CIL's visitors are inherently imperative (transformations are done in place). It is however perfectly possible to define visitors that maps an AST into another one, such as in Frama-C, which is based on CIL and offer in-place and copy visitor. Finally Cαml, an AST generator meant to easily take care of bound variables, generate map and fold visitors together with the datatypes.
If you have to write many different recursive operations over a set of mutually recursive datatypes (such as an AST) then you can use open recursion (in the form of classes) to encode the traversal and save yourself some boiler plate.
There is an example of such a visitor class in Real World OCaml.
The Visitor pattern (and all pattern related to reusable software) has to do with reusability in an inclusion polymorphism context (subtypes and inheritance).
Composite explains a solution in which you can add a new subtype to an existing one without modifying the latter one code.
Visitor explains a solution in which you can add a new function to an existing type (and to all of its subtypes) without modifying the type code.
These solutions belong to object-oriented programming and require message sending (method invocation) with dynamic binding.
You can do this in Ocaml is you use the "O" (the Object layer), with some limitation coming with the advantage of having strong typing.
In OCaml Having a set of related types, deciding whether you will use a class hierarchy and message sending or, as suggested by andreas, a concrete (algebraic) type together with pattern matching and simple function call, is a hard question.
Concrete types are not equivalent. If you choose the latter, you will be unable to define a new node in your AST after your node type will be defined and compiled. Once said that a A is either a A1 or a A2, your cannot say later on that there are also some A3, without modifying the source code.
In your case, if you want to implement a visitor, replace your EXPR concrete type by a class and its subclasses and your functions by methods (which are also functions by the way). Dynamic binding will then do the job.