# Proposal for Tchotchke.jl – everything but the kitchen sink

**URL:** https://discourse.julialang.org/t/proposal-for-tchotchke-jl-everything-but-the-kitchen-sink/13749
**Category:** Internals & Design
**Tags:** package
**Created:** [August 20, 2018, 6:06am UTC](https://discourse.julialang.org/t/proposal-for-tchotchke-jl-everything-but-the-kitchen-sink/13749 "2018-08-20T06:06:21Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![djsegal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djsegal/32/13752_2.png) [@djsegal](https://discourse.julialang.org/u/djsegal)
#### Post date: [August 20, 2018, 6:06am UTC](https://discourse.julialang.org/t/proposal-for-tchotchke-jl-everything-but-the-kitchen-sink/13749/1 "2018-08-20T06:06:21Z")

</div>

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,

> [@Why \`eye\` has been deprecated?](https://discourse.julialang.org/t/why-eye-has-been-deprecated/12824/66):
>
> Re: Matlab, I don’t think we need to copy the syntax, per se, but I’d like a `Batteries.jl` or `KitchenSink.jl` package that just reexports other packages to provide functionality on par with what’s built into Matlab.

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:

- [safe\_get - getfield without the errors](https://github.com/djsegal/Fussy.jl/blob/master/src/utils/safe_get.jl)
- [sort\_lists, shuffle\_lists, bisect\_reorder, in\_out\_reorder, etc.](https://github.com/djsegal/Fussy.jl/blob/master/src/utils/reorder_lists.jl)
- [filter\_approx! - filtering with tolerances](https://github.com/djsegal/Fussy.jl/blob/master/src/utils/filter_approx.jl)

* * *

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

---

<div class="post-metadata">

### Author: ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)
#### Post date: [August 20, 2018, 7:33am UTC](https://discourse.julialang.org/t/proposal-for-tchotchke-jl-everything-but-the-kitchen-sink/13749/2 "2018-08-20T07:33:49Z")

</div>

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

```julia-auto
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-auto
julia> length(missing)
ERROR: MethodError: no method matching length(::Missing)

julia> lift(length)(missing)
missing

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

```

### Nothing2missing

```julia-auto
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

```julia-auto
"""
    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)

```julia-auto

"""
    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](https://github.com/JuliaLang/julia/issues/24477)

## Environment variable reading functions

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

```julia-auto
"""
    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.

```julia-auto

"""
    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

```

---

<div class="post-metadata">

### Author: ![djsegal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djsegal/32/13752_2.png) [@djsegal](https://discourse.julialang.org/u/djsegal)
#### Post date: [August 21, 2018, 6:07am UTC](https://discourse.julialang.org/t/proposal-for-tchotchke-jl-everything-but-the-kitchen-sink/13749/3 "2018-08-21T06:07:13Z")

</div>

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

---

<div class="post-metadata">

### Author: ![djsegal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/djsegal/32/13752_2.png) [@djsegal](https://discourse.julialang.org/u/djsegal)
#### Post date: [June 10, 2020, 4:32pm UTC](https://discourse.julialang.org/t/proposal-for-tchotchke-jl-everything-but-the-kitchen-sink/13749/4 "2020-06-10T16:32:55Z")

</div>

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

> [@\[ANN\] Numerics.jl – The Extended Standard Library](https://discourse.julialang.org/t/ann-numerics-jl-the-extended-standard-library/40694/45):
>
> That might be achieved with something like this… (using [this TOML parser](https://github.com/JuliaLang/TOML.jl) for simplicity, but it could be easily written without any external dependency). using TOML deps = TOML.parsefile(Base.active\_project())["deps"] for package in keys(deps) @eval Main using $(Symbol(package)) end

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