Only engage when a name is used
I'm trying to finally transition from a custom startup.jl snippet to using BasicAutoloads, and another thing I'm struggling with is how new definitons are handled.
Basically, using a name should run the RHS, using .... But defining a new name shouldn't.
Eg, I should be able to do both:
# OK:
julia> mad([1,2,3])
│ Package StatsBase not found, but a package named StatsBase is available from a registry.
│ Install package?
│ (@v1.11) pkg> add StatsBase
└ Select environment:
<...>
1.4826022185056018
# (in a new REPL)
# shouldn't try importing the package:
julia> mad = "sane"
│ Package StatsBase not found, but a package named StatsBase is available from a registry.
│ Install package?
The snippet I'm using now does that by only handling function/macro calls, as in
if Meta.isexpr(ast, [:macrocall, :call]) && first(ast.args) ∈ symbolslist
...
In practice it works well, almost perfect :)
Oh, it's not just new definitions, BasicAutoloads in generally way too eager to engage!
julia> (mad="sane",)
│ Package StatsBase not found, but a package named StatsBase is available from a registry.
│ Install package?
😁
You might be looking for Autoloads.jl 😁. According to the README,
BasicAutoloads lets you say "whenever I type this in the REPL, run that for me"
A simple workaround is to only use distinctive symbols as triggers.
Alternatively, if this package were implemented at the string level rather than looking for symbols in the expression, then you could use "mad(" as a trigger. That would also trigger in comments, and if you type "ismad(x)" and "mad(x) = x.m". I don't like this approach.
Your recommendation would break this
julia> BasicAutoloads.register_autoloads([["γ"] => :(using Base.MathConstants)])
julia> γ
γ = 0.5772156649015...
and this
julia> BasicAutoloads.register_autoloads([["Unitful"] => :(using Unitful)])
julia> Unitful.m
m
both of which are reasonable usages.
Excluding anything syntactically immediately on the lhs of an assignment would not get any false negatives that I can think of and would address all the examples you named. It would, however, still give false positives on tuple de-structuring and function argument names.
Excluding anything syntactically on the lhs of an assignment (not just immediately) would give a false negative for StatsBase.mad(x::MyType) = x.precomputed_mad.
Excluding anything syntactically on the lhs of an assignment unless it is a function call or is on the LHS if a . or the RHS of a :: most things I've considered correct including:
Chairmarks.DEFAULTS.seconds = 1 triggering "Chairmarks"
StatsBase.mad(about::MyType) = x.precomputed_mad triggering "StatsBase", "mad", and "MyType" but not "about"
Unitful.m triggering "Unitful"
γ triggering "γ"
ismad not triggering "mad"
(mad="sane",) not triggering "mad"
mad="sane" not triggering "mad"
f(about) = about+1 not triggering "about"
but still gets some things wrong such as
getproperty(Chairmarks, :DEFAULTS).seconds = 1 not triggering "Chairmarks"
To get this totally correct would require a full understanding of user intent (which is impossible because there are a diversity of users) and a full parser (which is out of scope for this package).
I'm somewhat disinclined add a hackey, partially correct solution. I'm inclined to leave this issue unresolved and see if folks think this is a usability issue; if so I would consider adding some hacks to lessen (but not eliminate) the issue.
You might be looking for Autoloads.jl 😁
Maybe I should indeed try doing something like this... I guess I understand your vision of this package.
Please let me know what you think! https://github.com/JuliaAPlavin/Autoloads.jl
I would talk to @tecosaur about their ideas about autoloads; last I heard they had some pretty interesting ideas that might be worth taking on if you want to make a new package for folks who want something more fancy and/or customizable.