HighLevelTypes.jl

Author atoptima
Popularity
5 Stars
Updated Last
2 Years Ago
Started In
June 2018

HighLevelTypes

Build Status codecov Join the chat at https://gitter.im/realopt/Scanner.jl

The goal of HighLevelTypes.jl is to relieve the user from having to answer the question that we often face. Should this be a concrete or abstract type? This question is important because both have their own limitations.

  • For concrete types: Any behavior defined using concrete types can not be inherited. Sometimes you even don't know whether you will have in the future a specialization of your type or not. Take for example the case of Diagonal matrices defined here. Assume someone works with diagonal matrices such that all elements of a matrix are taking only 3 values. It is natural to create a new type that additionally stores those values. But since all the functions were defined for the concrete type Diagonal, it is not possible to reuse this behavior. And inheriting behavior is more important than inheriting fields.

  • For abstract types: If there is a field that would naturally fit in the abstract type, its definition needs to be delayed until the definition of the concrete types. This second issue is probably less important than the first one, although for some cases it makes the code really awkward.

As a high level language, Julia deserves a high level type. doesn't it?

What is a high level type ?

A high level type is an abstraction for two underlying types: one is abstract and one is concrete. The user only defines high level types. By default, the concrete type will be only used for instantiation.

@hl struct Person
    name::String
end
    
@hl struct Developer <: Person
    salary::Int32
end

@hl struct SepecializedDeveloper <: Developer
    language::String
end

function sumsalaries(first::Developer, second::Developer)
    return first.salary + second.salary
end

bob = Developer("Bob", 10000)
bob.name #returns "Bob" 
bob.salary #returns 10000

alice = SepecializedDeveloper("Alice", 15000, "Julia")    
alice.name # returns "Alice" 
alice.salary # returns 15000    
alice.language # returns "Julia"
    
sumsalaries(bob, alice) #returns 25000

How about performance ?

This is not the best choice for performance-critical code. Using abstract types instead of concrete types may increase the running time. Therefore the package provides the macro @concretify which can be applied on a block to use only the concrete types for all high level types within that block.

vec1 = Vector{Developer}()
push!(vec, bob) # OK
push!(vec, alice) # OK

@concretify vec2 = Vector{Developer}()
push!(vec2, bob) # OK
push!(vec2, alice) # throws MethodError (wrong concrete type for alice)

In particular, @concretify can be used to create concrete types.

@hl struct Job
    nb_hours::Int
    assigned_dev::Developer
end

Job(10, bob) # OK 
Job(100, alice) # OK

@concretify @hl struct ConcreteJob
    nb_hours::Int
    assigned_dev::Developer
end

ConcreteJob(10, bob) # OK
ConcreteJob(100, alice) # throws MethodError (wrong concrete type for alice)

Current limitations

  • A high level type can not have a tuple as its field (will be fixed soon).

Acknowledgment

This package was inspired by ConcreteAbstractions.jl