Rahul
Rahul

Reputation: 615

Is there a way to pass dependent fields to a Julia struct?

##define the struct
struct DataLoader
    getter::String
    DataLoader(getter="remote") = new(getter)
end
##testing the struct
ld = DataLoader("local")
ld.getter 

##local

ld = DataLoader()
ld.getter 

##remote

I'm trying to write a dataloader in Julia with some additional methods later defined on it.

The data can be loaded in two ways by the user - "remote" and "local".

If the user choses local, I need another field base_dir - the directory where the data is stored. If the user choses remote, I don't need base_dir as I know which URL to make the request to.

After that, when the loader has been defined, I'll call some functions on the loader to do other stuff (example function below):

function bar(x::DataLoader)
    if x.getter == "local"
        #do_somethings
        println("local found")

    elseif x.getter == "remote"
        println("remote found")

    else
        println("wrong mode passed")
        
    end  
end

My question is how do I define this co-dependency in the struct? That is, how do I tell the user and implement in my code the logic that if the getter is local I need base_dir and if it's remote, I can dynamically remove the field base_dir from my struct.

I'm new to Julia so any other tips on improving the code would also be more than welcome.

Upvotes: 3

Views: 410

Answers (1)

call me Steve
call me Steve

Reputation: 1727

I think there are several ways to do this; in my view, the simplest one is to rely on the dispatcher. This revolves around using two structs, one for "local", one for "remote". If really needed, you can create an "AbstractLoader" they both belong to, more on that at the end.

struct LocalLoader 
    basedir::String
end

struct RemoteLoader 
end

ll = LocalLoader("test_directory")
rl = RemoteLoader()

function bar(ll::LocalLoader)
    println("Base directory is $(ll.basedir)")
end

function bar(rl::RemoteLoader)
    println("This is the remote loader")
end

bar(ll)
bar(rl)

I see several advantages to this logic:

  • for the developer: the two methods can be developed independently
  • for the developer: you don't have to place some 'if' logic in your code which will remain simpler. (and it is not a possibility to pass an invalid mode). Said another way: you don't have to implement the dispatcher as you rely on Julia's dispatcher.
  • It clarifies the API to the user: when the user wants a local object a string must be passed. When the user wants a remote he/she can not pass the extra parameter.

The main drawback is code duplication. If there is some code duplication it can be countered, to do that you would need to make the two structs belong to an abstract type.

That would change the code in the following way:

abstract type  AbstractLoader end

struct LocalLoader <: AbstractLoader
    basedir::String
end

struct RemoteLoader <: AbstractLoader
end

ll = LocalLoader("test_directory")
rl = RemoteLoader()

function bar(ll::LocalLoader)
    println("Base directory is $(ll.basedir)")
end

function bar(rl::RemoteLoader)
    println("This is the remote loader")
end

bar(ll)
bar(rl)

function foo(al::AbstractLoader)
    println("Do common tasks")
end

foo(ll)
foo(rl)

Upvotes: 4

Related Questions