Proposal for Tchotchke.jl – everything but the kitchen sink

It’s nice that the Julia core library is small and efficient. But sometimes I just want to clutter up my workspace with a bunch of random functions.

Related to,

Can we put together a list of utility functions that prove to be useful from time to time?


These are some of the ones I’m playing around with at the moment:


I guess the call to action item for this post is, do you have any useful utility functions?

2 Likes

Just going through some of the utils.jl in my projects.

I don’t think these are the best ways to do these things, nor the nicest names etc for them.
I did some cleanup, just now and these may thus also have typos.
But for want of getting something out there:

lift: make functions that are not missing propagating into missing propagating

function lift(func)
    function(args...; kwargs...)
		if any(ismissing.(args)) || any(ismissing.(collect(values(kwargs))))
			missing
		else
			func(args...;kwargs...)
	    end
    end
end

Might be nice to have that in a macro from that applies it to all functions in a block.
Would be its own package then really, or added to Missing.jl

Example

julia> length(missing)
ERROR: MethodError: no method matching length(::Missing)

julia> lift(length)(missing)
missing

julia> lift(length)([1,2,3])
3

Nothing2missing

nothing2missing(::Nothing) = missing
nothing2missing(val) = val

sometimes libraries return nothing (i.e. programmers null),
but you know (using domain knowledge) that this actually should be interpreted as missing (statician’s null).
This kinda thing occurs when your are parsing some data.
Something similar occurs for regex, which is Union{RegexMatch, Nothing}.

Leaf Subtypes

Get all descendant concrete types

"""
    leaf_subtypes(T)
Returns all the nonabstract types decedent from `T`.
"""
function leaf_subtypes(T)
       if isleaftype(T)
           T
       else
           vcat(leaf_subtypes.(subtypes(T))...)
       end
end

Split path (opposite of joinpath)


"""
    splitpath(path)
The opposite of `joinpath`,
splits a path unto each of its directories names / filename (for the last).
"""
function splitpath(path::AbstractString)
   ret=String[]
   prev_path = path
   while(true)
       path, lastpart = splitdir(path)
       length(lastpart)>0 && pushfirst!(ret, lastpart)
       length(path)==0 && break
       if prev_path==path
            # catch the casewhere path begins with a root
            pushfirst!(ret, path)
            break
       end
       prev_path = path
   end
return ret

See also `splitpath` to match `joinpath` · Issue #24477 · JuliaLang/julia · GitHub

Environment variable reading functions

Safer than reading environment variables directly; as has inbuilt default.
Also handles user weirdness better.

"""
    env_bool(key)
Checks for an enviroment variable and fuzzy converts it to a bool
"""
env_bool(key, default=false) = haskey(ENV, key) ? lowercase(ENV[key]) ∉ ["0","","false", "no"] : default

"""
    env_list(key)
Checks for an enviroment variable and converts it to a list of strings, sperated with a colon
"""
env_list(key, default=String[]) = haskey(ENV, key) ? split(ENV[key], ":") : default

User input getting functions

Keep looping til user gives valid input.


"""
    bool_input
Prompted the user for a yes or no.
"""
function input_bool(prompt="")::Bool
    input_choice(prompt, 'y','n')=='y'
end

"""
    input_choice
Prompted the user for one of a list of options
"""
function input_choice(prompt, options::Vararg{Char})::Char
    while(true)
        println(prompt)
        println("["*join(options, '/')*"]")
        response = readline()
        length(response)==0 && continue
        reply = lowercase(first(response))
        for opt in lowercase.(options)
            reply==opt && return opt
        end
    end
end

"""
    input_choice
Prompts the user for one of a list of options.
Takes a vararg of tuples of Letter, Prompt, Action (0 argument function)
Example:

    input_choice(
        ('A', "Abort -- errors out", ()->error("aborted")),
        ('X', "eXit -- exits normally", ()->exit()),
        ('C', "Continue -- continues running", ()->nothing)),
    )

"""
function input_choice(options::Vararg{Tuple{Char, <:AbstractString, Any}})
    acts = Dict{Char, Any}()
    prompt = ""
    chars = Char[]
    for (cc, prmt, act) in options
        prompt*="\n [$cc] $prmt"
        push!(chars, cc)
        acts[lowercase(cc)] = act
    end
    prompt*="\n"

    acts[input_choice(prompt, chars...)]()
end
10 Likes

Just wow. Nothing to missing is a pretty profound sentiment. tchotchkeyed

2 Likes

It’s been a couple years and people responded a decent amount to:

What new tchotchkes do you all have in your util and startup files?

2 Likes