Roman Dimov
Roman Dimov

Reputation: 33

Literal Types in Julia

What is the best pattern to create literal types in Julia?

In Typescript, you can make types out of string literals:

type command = "start" | "end" | "pause";

In Julia, this would allow you to use multiple-dispatch with string arguments, making it easy to extend cases.

An example of this would look like

function process(::"start") 
    initialize()
    do_something()
    ...
end

function process(::"end")
    teardown()
    ...
end

function process(::"pause")
    temporary_pause()
    ...
end

Currently, I am using a workaround similar to this

struct CommandType{T}
end

CommandType(s::AbstractString) = CommandType{Symbol(s)}()

macro CommandType_str(s)
  return CommandType{Symbol(s)}
end

function process(t::CommandType"start")
  ...
end

function process(t::CommandType"stop")
  ...
end

function process(t::CommandType"pause")
  ...
end

process(command::String) = process(CommandType(command))

# Usage
process("start")

Is there a more succinct pattern I can use? Is it possible that literal types will be added to Julia in the near future?

Upvotes: 3

Views: 351

Answers (3)

raythurnevoid
raythurnevoid

Reputation: 2824

The closest thing i managed to get without going crazy and without polluting the scope of hoisted symbols is:

module Extraction
@enum Type log_stats_by_author commits_by_author
end

function extract_git_stats(extraction_type::Extraction.Type)
...
end

extract_git_stats(Extraction.commits_by_author)

This also works with intellisense in Julia's vscode extension, solutions using Symbol/Val won't provide any autocompletion:

enter image description here

Upvotes: 0

user19242326
user19242326

Reputation:

I would take another approach to your problem, don't rely on string or symbols to determine which function to use, instead write functions that have clear names.

For example, your function is named process() and takes a start command. What I'm I processing? Do I do something with that command in the function?

Instead what if I wrote:

function start_service(# pass required arguments)
   ...
end 

Sounds better, I know what I'm starting and the necessary arguments, if any, would make sense.

Later on, I decide I need to write data to a file, I now have to update the Command, perhaps, write?

function process(::CommandType"write",path::AbstractString,data)
    # What is being written? Error log? Data?
end

It would be cleaner to write:

write_data_to(path::AbstractString)

Upvotes: 1

Bogumił Kamiński
Bogumił Kamiński

Reputation: 69949

As far as I know the current practice is essentially what you have developed. To be concrete it is to use Val with Symbol argument. So your function signature would be e.g. process(::Val{:start}) and you would call it with process(Val(:start)) (of course you could use a utility function or macro to generate Val(:start) from a string as in your code if you wanted)

Upvotes: 4

Related Questions