Hi Anonymous,

Whether all this is worth it depends on how you're going to use these objects. 
Particularly if you're likely to need to work with long lists of cars of 
different types, the path you're following is probably not worthwhile. The 
issue is this: if you have a container where julia knows the type of each 
element in advance (all objects in the container have the same *concrete* 
type), then julia can emit efficient code for processing the whole list. In 
such 
cases, this path is worthwhile. The other place where it might be worthwhile 
is if you have an enormous amount of computation you're going to do on each 
object, and that processing is greatly sped up by having "sub-functions" that 
know the full type details. (In such cases you'll want a function barrier, 
http://docs.julialang.org/en/stable/manual/performance-tips/#separate-kernel-functions.)

If in contrast item[i+1] has a different type than item[i], and the amount of 
processing is quite modest, then it may not be worth it. Because julia can't 
predict the type at compile-time, it has to look up the type at run-time, 
search for the appropriate method in method tables, decide (via type 
intersection) which one matches, determine whether it has been JIT-compiled 
yet (and do so if not), and then make the call. You're asking the full type-
system and JIT-compilation machinery to basically execute the equivalent of a 
switch statement or dictionary lookup in your own code. Julia can do this, but 
it's a lot of churn under the hood. If this is the situation you're in, it 
seems likely to be better to just write that switch statement or to use a 
dictionary.

Julia emphasizes the "real" use for types---being able to make important 
decisions at compile time---rather than the "window dressing" (glorified switch 
statements) uses that some OOP paradigms seem to encourage.

Best,
--Tim

On Friday, April 15, 2016 11:14:42 AM Mauro wrote:
> On Fri, 2016-04-15 at 09:56, Anonymous <[email protected]> wrote:
> > I need the fields color and year to be Int and ASCIIString, respectively,
> > and I can't just make the types Color and Year type aliases of Int and
> > ASCIIString, since I need these abstract types to distinguish different
> > types of Car for the purposes of multiple dispatch.
> 
> Maybe you need to introduce your Color and Year types.  However if they
> alias to different types then your good.
> 
> typealias MyYear Int
> typealias MyColor ASCIIString
> 
> Car(y::MyYear) = Car{Void, Year}(nothing, y)
> Car(c::MyColor) = Car{Color,Void}(c, nothing)
> Car(c::MyColor, y::MyYear) = Car{Color,Year}(c, y)
> 
> (Note though that this will construct types which have abstract
> field-types.  Those are bad for performance (but only worry about this
> in critical code), have a look at the performance section of the docs)
> 
> > Basically let's say I have 6 possible features and I want to include
> > features 3, 5 and 6.  And let's say those features are ASCIIString, Int
> > and
> > Int, respectively, then I want to be able to write:
> > 
> > Car{Feature3, Feature5, Feature6}(a, b, c)
> 
> Well, outer constructors cannot work like this.
> 
> > where a is a string, and b and c are both integers, and then I want the
> > constructor to return:
> > 
> > Car{Void, Void, Feature3, Void, Feature5, Feature6}(nothing, nothing, a,
> > nothing, b, c)
> > 
> > On Friday, April 15, 2016 at 12:18:56 AM UTC-7, Mauro wrote:
> >> On Fri, 2016-04-15 at 07:28, Anonymous <[email protected] <javascript:>>
> >> 
> >> wrote:
> >> > OP here,
> >> > 
> >> > So it looks like the consensus is to use a single type with un-used
> >> > features set to nothing.  I've actually been playing around with this
> >> > approach since I posted this question.  Here's what I've got:
> >> > 
> >> > abstract AbstractCar
> >> > 
> >> > abstract Color
> >> > abstract Year
> >> > 
> >> > typealias ColorOrVoid Union{Color, Void}
> >> > typealias YearOrVoid Union{Year, Void}
> >> > 
> >> > type Car{C<:ColorOrVoid, Y<:YearOrVoid} <: AbstractCar
> >> > 
> >> >   color::typeMap(C)
> >> >   year::typeMap(Y)
> >> > 
> >> > end
> >> 
> >> This should work:
> >> 
> >> type Car{C<:ColorOrVoid, Y<:YearOrVoid} <: AbstractCar
> >> 
> >>   color::C
> >>   year::Y
> >> 
> >> end
> >> 
> >> (Aside:
> >> I think using a function inside the type definition is only possible if
> >> it can be evaluated at compile time (I might be wrong though).  Consider
> >> this example:
> >> 
> >> julia> g(T) = Vector{T}
> >> g (generic function with 1 method)
> >> 
> >> julia> type B{T}
> >> 
> >>        b::g(T)
> >>        end
> >> 
> >> julia> B([1,2])
> >> B{Int64}([1,2])
> >> 
> >> julia> h(T) = string(Int.name.name)[1]=="I" ? Int : Float64
> >> h (generic function with 1 method)
> >> 
> >> julia> h(AbstractString)
> >> Float64
> >> 
> >> julia> type C{T}
> >> 
> >>        c::h(T)
> >>        end
> >> 
> >> WARNING: static parameter T does not occur in signature for call at
> >> none:2.
> >> The method will not be callable.
> >> 
> >> end-aside)
> >> 
> >> > where the function typeMap will send Void to itself, Color to
> >> 
> >> ASCIIString
> >> 
> >> > and Year to Int.  However I tried doing this and I got an error, I
> >> 
> >> probably
> >> 
> >> > the way to do this is with meta-programming and macros, but I'm not
> >> > sure
> >> > how since I'm a complete novice at meta-programming.
> >> > 
> >> > I would also like to have outer constructors which allow me to avoid
> >> 
> >> having
> >> 
> >> > to enter Void for all the un-used features, so if I'm only interested
> >> > in
> >> > Year, I would have an outer constructor of the form:
> >> > 
> >> > Car{Year}(y::Int) = Car{Void, Year}(nothing, y)
> >> > 
> >> > However this gives me an error saying that *static parameter Year does
> >> 
> >> not
> >> 
> >> > occur in signature for call at none*.
> >> 
> >> Outer constructors are a bit hard to grok because type parameter feature
> >> twice, in the same location, but have a completely different
> >> meaning:
> >> 
> >> Car{Year}(y::Int) = Car{Void, Year}(nothing, y)
> >> 
> >>     ^^^^                ^^^^^^^^^^
> >>  
> >>  function parameter     type parameters
> >> 
> >> Function parameters need to be inferred from the arguments, whereas type
> >> parameters are part of the type. (this might get cleared up in the
> >> future)
> >> 
> >> Consider that for just an ordinary function, you cannot do this:
> >> 
> >> f{T<:Int}(i::I) = 5
> >> # now call it like so
> >> f{Int}(4) # error
> >> 
> >> Anyway, the solution is just (assuming Color and Year can be
> >> distinguished by type):
> >> 
> >> Car(y::Year) = Car{Void, Year}(nothing, y)
> >> Car(c::Color) = Car{Color,Void}(c, nothing)
> >> Car(c::Color, y::Year) = Car{Color,Year}(c, y)
> >> 
> >> If you want to explicate specify the type then (like is used in
> >> Julia-Base for e.g. array constructors):
> >> 
> >> Car(T::Type{Year}, y) = Car{Void, Year}(nothing, y)
> >> ...
> >> 
> >> > On Thursday, April 14, 2016 at 10:08:53 PM UTC-7, Toivo Henningsson
> >> 
> >> wrote:
> >> >> As you say, it's a lot of types. If you would really need to
> >> 
> >> instantiate
> >> 
> >> >> an exponential number of types then maybe you should reconsider,
> >> 
> >> because
> >> 
> >> >> the jit compiler has to do quite a lot of work for each type that is
> >> 
> >> used.
> >> 
> >> >> But if you're not actually going to instantiate such a humongous
> >> >> number
> >> 
> >> of
> >> 
> >> >> them, or if you really want to be able to use specialization and
> >> 
> >> dispatch
> >> 
> >> >> in this way: How about a middle road where you use a parametric type
> >> 
> >> and
> >> 
> >> >> set the types of all unused fields to Void (the type of nothing)? That
> >> >> should be able to support the cases that you mentioned.
> >> >> 
> >> >> Also, in many cases, storage for a value worth known type of eg Void
> >> >> is
> >> >> free, since it is known that there is only one instance. The exception
> >> 
> >> is
> >> 
> >> >> if the value could be uninitialized as well.

Reply via email to