- Sponsor
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Require constructors and convert
to return objects of stated type?
#42372
Comments
How would this work for e.g.
? Would that also be restricted? I'm thinking of this because there's a lazy shuffle in a package of mine, which tries to shuffle iterators around. For |
Yes, under this proposal that would be a bad constructor. You could define |
Very good idea. There are legitimate uses of this anti-pattern, but I think they're limited to things like compatibility shims. It's also important to mention that the front-end is already allowed to assume this property of |
It'd be great if we can remove or rephrase this in the manual
I don't think it's OK in any cases for new APIs. Keeping This is an actionable item within the 1.x time frame.
I'm curious what other legitimate uses. My belief has been there's practically none. Can't you do a compatibility shim with a factory function? I guess you'd need a type for |
I've finally discovered a case where I plan to exploit the current behavior; I still think we should change this for Julia 2.0, but the counterexample is interesting and worth knowing about. In an attempt to resolve #44538 (comment) and what to do about that PR in general, I am planning to "hamstring" Static.jl and see what breaks (as a way of figuring out where we may be getting constprop failures, and thus how badly we actually need StaticInt): diff --git a/src/Static.jl b/src/Static.jl
index 82ab248..0762c33 100644
--- a/src/Static.jl
+++ b/src/Static.jl
@@ -35,10 +35,10 @@ A statically sized `Int`.
Use `StaticInt(N)` instead of `Val(N)` when you want it to behave like a number.
"""
struct StaticInt{N} <: StaticInteger{N}
- StaticInt{N}() where {N} = new{N::Int}()
- StaticInt(N::Int) = new{N}()
+ StaticInt{N}() where {N} = N # new{N::Int}()
+ StaticInt(N::Int) = N # new{N}()
StaticInt(@nospecialize N::StaticInt) = N
- StaticInt(::Val{N}) where {N} = StaticInt(N)
+ StaticInt(::Val{N}) where {N} = N # StaticInt(N)
end As you can see, this causes the I am thus only using this for investigation/debugging; I would never want to release code like this. So I'm still in favor of this proposal, but one wonders if one would like to be able to start Julia in a sloppier mode. Of course keeping such rarely-used functionality working would be its own challenge, so I am very unsure about whether that's really practical. Anyway, food for thought if/when we do get around to considering a Julia 2.0 release. |
Oh, that's a good find! In my endless optimism I'd like to interpret that as an argument for having the staticness/require-a-compile-const be a seperate feature from any specific type, but maybe that's just my friendliness towards Zig |
I think I've found a usecase where guaranteeing not to return a # define a struct to act as a handle for our resource
struct CommunicationBus
mutex::Semaphore
# override the default constructors & require a Semaphore
function CommunicationBus(f)
# acquire the relevant mutex before we can construct an instance
# ignoring error handling for the example etc.
acquire(BUS_MUTEX)
bus = new(BUS_MUTEX)
try
# pass the instance into our function, guaranteeing that
# f has exclusive access to the specific resource
f(bus)
finally
# release the mutex, no matter how we happen to exit f
# and as soon as we do exit f
release(bus.mutex)
end
nothing
end
end which explicitly doesn't return a This would then be used like function foo()
CommunicationBus() do bus
# communicate over the bus object, we have exclusive access!
write(bus, 0x00)
end
end The advantages of this over e.g. a finalizer is that the mutex is guaranteed to be released as soon as possible (and not deferred to whenever GC happens to run), as well as not having to incur a heap allocation due to not being mutable. One possibility if constructors were required to return an object of their stated type could be to rely on not providing an actual constructor at all. More or less changing the above struct to function acquire_bus end
struct CommunicationBus
mutex::Base.Semaphore
function acquire_bus(f)
acquire(BUS_MUTEX)
bus = new(BUS_MUTEX)
try
f(bus)
finally
release(bus.mutex)
end
nothing
end
end so that |
You just need |
Right, that's the trick! It's not really a pretty solution, but it's definitely a usable workaround. For completeness: struct _RAIIBus
mutex::Base.Semaphore
global CommunicationBus
function CommmunicationBus(f)
acquire(BUS_MUTEX)
bus = new(BUS_MUTEX)
try
f(bus)
finally
release(bus.mutex)
end
nothing
end
end It's such a small difference though that the benefits of requiring the actual constructor to return a proper object vastly outnumber the upsides of keeping this around for RAII! |
(This is a Julia 2.0 proposal, as it is breaking. It is surely not novel, but I don't see another such issue open.) We currently treat outer constructors and
convert
method definitions as any other generic function, but there's an argument to be made that they should be special-cased by requiring that they always return an object of the stated type. Motivations:Why? Because someone might define
in which case you get
and StackOverflowErrors are to be avoided: they can take forever to resolve in some cases, they can at least in principle trash your session, etc.
The only good way to write such a fallback method is
but I will bet that the vast majority of developers do not know this or bother with it, and probably everyone finds it ugly and annoying.
Some might complain about idempotent operators and involutions, for example one might be tempted to define
But a solution is to separately introduce
not
fromNot
, and allow this behavior fornot
but notNot
(Not
must always wrap in anotherNot
layer).The text was updated successfully, but these errors were encountered: