What's in your @?

Simple enough, what’s in your default environment? Could be fun to guess what everyone primarily does based only on their @s. What do you think I do?

julia -e "Pkg.status()"
Status `~/.julia/environments/v1.11/Project.toml`
  [28312eec] Alert v1.2.0
  [6e4b80f9] BenchmarkTools v1.6.0
  [052768ef] CUDA v5.8.2
  [e30172f5] Documenter v1.11.4
  [35a29f4d] DocumenterTools v0.1.20
  [bdcacae8] LoopVectorization v0.12.172
  [5fb14364] OhMyREPL v0.5.31
  [14b8a8f1] PkgTemplates v0.7.55
  [91a5bcdd] Plots v1.40.13
  [27ebfcd6] Primes v0.5.7
  [295af30f] Revise v3.8.0
  [5e0ebb24] Strided v2.3.0
  [ac1d9e8a] ThreadsX v0.1.12
3 Likes
Status `~/.julia/environments/v1.11/Project.toml`
  [16fef848] LiveServer v1.5.0
  [1e6cf692] TestEnv v1.102.1

If I need BenchmarkTools or Documenter or ControlPlots I put them into the test environment of my project.

1 Like

So minimal! I take it you do a lot of web dev then?

No. I need LiveServer to view the documentation of my own packages. Like this:

Script build_docu.jlin the scripts folder of my packages:

# build and display the html documentation locally
# you must have installed the package LiveServer in your global environment

using Pkg

function globaldependencies()
    projectpath = Pkg.project().path
    basepath, _ = splitdir(projectpath)
    Pkg.activate()
    Pkg.update()
    globaldependencies = keys(Pkg.project().dependencies)
    Pkg.activate(basepath)
    globaldependencies
end

if !("LiveServer" in globaldependencies())
    println("Installing LiveServer globally!")
    run(`julia -e 'using Pkg; Pkg.add("LiveServer")'`)
end

if !("Documenter" ∈ keys(Pkg.project().dependencies))
    using TestEnv
    TestEnv.activate()
end
using LiveServer; servedocs(launch_browser=true)
1 Like
(@v1.11) pkg> st
Status `~/.julia/environments/v1.11/Project.toml`
  [09cdc199] BasicAutoloads v1.0.4
  [295af30f] Revise v3.8.0
  [27c01619] ShareAdd v2.2.0

For everything else there is ShareAdd.jl:

julia> ShareAdd.info()
  @About
   => ["About"]
  @BenchmarkTools
   => ["BenchmarkTools"]
  @DataFrames
   => ["CSV", "DataFrames", "Missings", "StringEncodings"]
  @DefaultApplication
   => ["DefaultApplication"]
  @DocumenterTools
   => ["DocumenterTools"]
  @Makie
   => ["Bonito", "CairoMakie", "GLMakie", "Makie", "Observables", "WGLMakie"]
  @Math
   => ["SpecialFunctions", "StatsBase"]
  @Mendeleev
   => ["Mendeleev"]
  @PackageMaker
   => ["Blink", "PackageMaker"]
  @Plots
   => ["Plots", "StatsPlots"]
  @Serializations
   => ["JLD2", "JSON3"]
  @StaticArrays
   => ["StaticArrays"]
  @TestEnv
   => ["TestEnv"]
  @TestingTools
   => ["Coverage", "ReTest", "TestItemRunner", "TestItems", "TestSetExtensions", "WhyNotEqual"]
  @ThisNThan
   => ["AutoHashEquals", "Chevrons", "FilePathsBase", "FloatingTableView", "OhMyREPL", "QML", "TableView"]
  @Unitful
   => ["Unitful"]
  @v1.11
   => ["BasicAutoloads", "Revise", "ShareAdd"]
8 Likes

Up until a week ago, literally nothing – I only used project environments.

Recently, I started keeping DaemonMode.jl in my default environment, which I stack on top of project environments, for use with Claude Code.

2 Likes

Oh interesting I’ve never heard of your ShareAdd.jl. I’ve tried multiple shared environments before but exactly found it to be more of a hassle than it benefitted. I’ll have to check this out!

DaemonMode.jl looks pretty cool! My first thought it that it’s kind of a non-trivial “hello world” of multiprocessing. Now that I think about it, it could also be useful for tmux-like persistent computations after you leave ssh?

Yeah – just keep DaemonMode.jl running in the background, and then you can execute scripts against it without having to go through standard/first time compilation all over again.

I find it particularly helpful with other programs that want to call Julia – DaemonMode makes it pretty easy to just keep a single Julia process rather than restarting it every time.

I was initially worried about the fact that it hasn’t had releases in a while, but it works just fine.

2 Likes

What kind of programs do you mean?

Agentic LLMs, like Claude Code.

By default, an agent will run a script as julia my_script.jl, see what’s broken, and then try to fix it. This is slow because of startup time + recompiling functions. By starting a Daemon and then running scripts, the test feedback loop is much faster.

nohup julia --startup-file=no -e "using Revise; using DaemonMode; serve(;shared=true)" > daemon.log 2>&1 &
julia --startup-file=no -e "using DaemonMode; runargs()" script.jl
4 Likes
(@v1.11) pkg> st
Status `~/.julia/environments/v1.11/Project.toml`
  [5fb14364] OhMyREPL v0.5.31
  [21216c6a] Preferences v1.4.3
  [295af30f] Revise v3.8.0
  [ac92255e] Speculator v0.2.0
  [0c614874] TerminalPager v0.6.2
  [3fa0cd96] REPL v1.11.0

I need to get around to making a system image for these :stuck_out_tongue:

1 Like
pkg> st
ERROR: no active project

because I disable this feature in my startup.jl:

# Remove global environment from load path
let
    i = findfirst(==("@v#.#"), LOAD_PATH)
    if !isnothing(i)
        deleteat!(LOAD_PATH, i)
    end
end

Too many times I have added packages in the global environment by mistake, and then I forgot to add them in the local environment (because it was working!), resulting in non-reproducible projects and possibly corrupted state where incompatible packages where used together. Because as the documentation says:

Packages in non-primary environments can end up using incompatible versions of their dependencies even if their own environments are entirely compatible. This can happen when one of their dependencies is shadowed by a version in an earlier environment in the stack (either by graph or path, or both).

I’ve ranted about this before (a bit too vehemently) here. The gist of it is that I don’t understand why Julia would use incompatible package versions by default, instead of showing an error and maybe having an opt-in setting to allow it (suggested name: yolo=yes).

4 Likes

I’ve never heard of either of these. What do you do with them?

That definitely makes sense. Though the biggest one on the other side for me is Plots.jl. I usually want to plot stuff when I’m testing functionality/making examples/actually doing something with it, but don’t want the package itself to directly depend on Plots.

TerminalPager.jl is invaluable for reading long docstrings or outputs, it’s practically the first package I install in any new Julia version. A lot of people also use it for viewing tabular data (eg. DataFrames), though I generally go straight to Jupyter or Pluto for those.

My preferred way of using TerminalPager is to use the | key to change the REPL mode: it shifts into pager> mode after that, and any output that occurs in that mode, if it’s longer than one screen length, gets displayed in pager interface i.e. like less on Linux where you can scroll up and down, search, etc. If you press ? when you’re in pager mode, you get to pager-help mode where the docstrings are displayed the same way. Because Julia’s functions often have many methods, there ends up being screenfuls of docstrings even if each individual doc is pretty short. So TerminalPager makes those docs a lot more manageable and easier to parse.

(Speculator seems to be Jakob’s own package, that I also learnt about from this thread, so I’ll just link to its announcement post that I found today.)

1 Like

@digital_carver’s description of TerminalPager.jl is excellent and much better than I could give!

Speculator.jl is indeed one of my own packages. It has a feature where it tries to precompile methods in a background thread of your REPL. I’m not sure to what extent this feature is actually useful, but I’m at least using it to ensure it doesn’t break.

@Eben60 I’m curious about the combination of BasicAutoloads.jl and ShareAdd.jl here. Is your setup such that typing DataFrame() will automatically load your @DataFrames shared environment and work?


EDIT: Found the answer (yes!):

This sounds really nice, but I wish I didn’t need to enter a special mode to get this, which imo should be default behavior.

Is it theoretically possible for a package to override the default behavior and enable this instead?