Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @typeconst #54117

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open

Conversation

antoine-levitt
Copy link
Contributor

See #54114
I'd like it to support @typeconst x = 3.1; @typeconst x = 3, as well as x = 2; @typeconst x=3.2, but this requires figuring out if x already has a type assertion, and I don't know how to do that...

base/util.jl Outdated
ex isa Expr || return ex
if ex.head === :(=)
quote
tmp = $(esc(ex.args[2]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I wrote in https://giordano.github.io/blog/2022-06-18-first-macro/, you want to mark tmp as local to avoid leaking the name to the calling namespace

Suggested change
tmp = $(esc(ex.args[2]))
local tmp = $(esc(ex.args[2]))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I figured it was OK because it was gensymed (I had a previous version that called gensym() directly), but you're right it's even better not to leak it at all!

@jonas-schulze
Copy link
Contributor

This macro is perhaps better suited for a package.

See also #44259 as well as this old comment by you: #43671 (comment)

@antoine-levitt
Copy link
Contributor Author

As long as const is scary-looking and frowned upon I think it makes sense to have this in Base as a blessed way to support this type of workflow.

@KristofferC
Copy link
Sponsor Member

aiming to replace the widespread use of const to improve the performance of globals

I don't get it. Why replace const? const is stricter than a typed global and is better for correctness and performance if what you want is actually const.

@antoine-levitt
Copy link
Contributor Author

Const is very scary, and gives you absolutely no correctness guarantee in both in its docstring and in warnings, so it'd be good to have something less scary. I've asked before (I don't remember where) whether there was a path towards making const less scary, but that didn't seem like a likely prospect at the time.

@KristofferC
Copy link
Sponsor Member

Just saying, the premise that this PR seems to build on (trying to replace consts with non-consts) is not gonna fly very far. I would suggest making it solely about a convenience of making a global with the type of an RHS and remove the references to const.

@StefanKarpinski
Copy link
Sponsor Member

How is const scary? If your program runs without warnings then there are no worries. If your program runs and has warnings then yes, you should be concerned, but that is always true, not just of const. Can you explain what your actual worry about using const is?

@antoine-levitt
Copy link
Contributor Author

"In some cases changing the value of a const variable gives a warning instead of
an error. However, this can produce unpredictable behavior or corrupt the state of your program,
and so should be avoided."

How is that not scary? By the way, the docstring doesn't mention that redefining const variables (eg const x=3.14 sometimes doesn't give a warning. I don't understand this as "if the code gives no warning, then it's fine" like you imply but more as "sometimes illegal behavior is not detected as an error or even a warning". If redefining things in a way that doesn't generate a warning is fine, can we update the docstring with more guarantees?

Also, redefining const x=[1] gives a scary warning.

Also also, every so often somebody comes up with even scarier things like redefining const is illegal and let's forbid it altogether.

But of course my main issue with const is that it's terrible semantics. People could be forgiven for thinking that const are actually, you know, constant. Abusing this purely for performance is fundamentally a hack.

@StefanKarpinski
Copy link
Sponsor Member

This really feels like trying to manufacture fear, uncertainty and doubt where there isn't any real reason for it. The bottom line is: if your code emits warnings then you should be concerned, if it doesn't emit warnings then you do not need to worry. If you don't like being able to redefine constants, don't do it and you can forget about this entirely. There's absolutely nothing wrong with using const and we should encourage it rather than discouraging it. Trying to drum up fear based on the fact that we allow redefining and even changing const in some cases for interactive convenience is really unreasonable.

@StefanKarpinski
Copy link
Sponsor Member

Abusing this purely for performance is fundamentally a hack.

Where is this coming from? Nobody has ever suggested abusing const for performance. You should make things const if they are actually constant. Allowing the redefinition of constants in some situations is only allowed for convenience in interactive usage. No program should redefine a constant in its normal course of usage, and if it does, it prints a warning. What part of "your program should not emit warnings" is so hard to understand? If you don't like the ability to redefine constants and it scares you, then feel free to not do that. Then you can forget about this issue entirely.

@antoine-levitt
Copy link
Contributor Author

I understand people object to the wording of the news, I've changed it.

To be clear, I'm talking about the use of const purely to improve performance, the workflow being to have a file with something like

const param = 3.14
function f(x)
    param+x
end
println(f(2))

and then include(file.jl) at the REPL. Then, you edit the file to redefine the parameter and run the script again, until you get the result you want, then you move the code to somewhere more permanent (or just throw it away). It's the workflow I've been using for literally 10 years now and recommending to people, while always feeling slightly uneasy about it. I want to emphasize how useful this type of workflow is. I've never had any problems with it, despite the warnings, but it's not ideal (especially for new users) . Unfortunately, all the alternatives I know to this workflow are worse.

I was motivated by #54114 and the lack of appetite for making const redefinition be an "officially supported" thing to try to improve the situation, hence this PR.

I really don't understand the situation here. It's certainly not my intention to manufacture FUD but to try to remove some. Is const redefinition supported with well-defined semantics, and will it continue to be for the foreseeable future? If yes, can we make its docstring less scary and vague (while still keeping the warning), something like "const redefinition is supported to help interactive usage, but it is the user's responsability to ensure that any function that uses the const global is redefined"? If no, shouldn't we warn users away from it more vehemently and design replacements?

@StefanKarpinski
Copy link
Sponsor Member

Please ignore #54114. The parties responsible have been flogged. We are not going to make redefining constants illegal. If you also redefine all the functions that rely on a constant, as you do in the workflow you're describing, then it works reliably. A more likely direction is that #40399 will happen—I'm pretty sure this is going to happen as soon as someone gets around to it—at which point redefining a constant will become less scary: code that was compiled to rely on the old value will continue to work with the old value, while newer code will use the new value. This will eliminate any "undefined behavior" whether functions are redefined or not. Constants will cease to be "constant" and simply be bindings that are very expensive to redefine.

@antoine-levitt
Copy link
Contributor Author

A more likely direction is that #40399 will happen

That would be awesome, and in that case there would be a lot less need for this PR. Let's see how that develops.

@StefanKarpinski
Copy link
Sponsor Member

This PR is good though—@typeconst x = <expr> is a useful feature in-and-of-itself and worth having, imo.

@vchuravy
Copy link
Sponsor Member

I do think the name of the macro needs to change. It has nothing to do with const. IIUC it's lhs::typeof(rhs) = rhs and we call those typed globals.

So maybe @typed?

@StefanKarpinski
Copy link
Sponsor Member

Eh, the type is constant 🤷🏻‍♂️. Kinda makes sense...

@jonas-schulze
Copy link
Contributor

I think a separate package would be a better place for this macro.

Just this week I got bitten again by needing to find out which version of Compat I needed to support a certain feature on the LTS. If I had needed using TypedVariables (or whatever the name) even in the most recent Julia, it would work even on the LTS. Arguably, the macro could be back-ported to the current (and/or soon-to-be 🤞) LTS, but creating a separate package may be less work for everyone involved.

Your package would likely be about as small as UnPack. IMO, this is not a bad thing. It only means that the hurdle of adding the package as a dependency is very low; similar to the >180 packages using UnPack.

@antoine-levitt
Copy link
Contributor Author

@typed is too generic to me (eg possible confusion with @inferred, @code_typed etc)

I don't see why moving to a package would be better for such a simple thing. It can be added to Compat easily and it's not meant to be something depended on by packages, more for interactive use - I considered moving it to InteractiveUtils, but figured it'd be confusing to people if it worked at the REPL and not in a package.

@KristofferC
Copy link
Sponsor Member

It would be cute if this could be written as something like x::auto = y. It just feels like the big @typeconst becomes so "overwhelming" here compared to what it would be typically annotated to.

@antoine-levitt
Copy link
Contributor Author

I agree typeconst is too big, that's why I added support for

@typeconst begin
    x = 1
    y = 2
end

to support the "big block of parameters" style.

We don't have auto as a keyword, but plausibly we could try to parse x::const = 2, which would make sense I guess? I don't know how to do that though.

@jonas-schulze
Copy link
Contributor

As you are thinking about new syntax, I would like to suggest x := 42 (#44259).

@StefanKarpinski
Copy link
Sponsor Member

I do rather like := syntax for this. Should work in local or global scope, of course.

@sefffal
Copy link

sefffal commented Apr 26, 2024

If we're volunteering new syntax, what about a slight tweak:

myvar::@auto = 1.0

assuming that parsed somehow. To me it makes it clear that it's doing a small code transformation like any macro.

I'm sure there are many other uses for macros in that position.

@antoine-levitt
Copy link
Contributor Author

I would have thought := would be too precious to waste for this, it's still a relatively marginal use... and if const ever becomes more flexible we might want to save it for const?

I'm sure there are many other uses for macros in that position.

Really? I don't see it... Since :: does mean typing it would have to have something to do with types, and that's a pretty limited space...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants