Open baronfel opened 7 years ago
I would love to have this. In particular it's useful for the pattern of picking a certain set of operator overloads in some tight context.
This was discussed a bit on Reddit. There's a lot of utility here and it feels fairly natural. One issue is that a motivating use case is being able to remove the full qualifications on collection module functions. However, these are marked as RQA, so "opening List" wouldn't be possible. There would be three outcomes:
open
declarations bypass RQAopen
them only in the type and function scopeI would personally prefer (1).
@cartermp for what it’s worth, I also have a strong preference for option one. I’m not keen on weakening the effectiveness RQA.
Tangentially, there was a recent Twitter thread where folks voiced a desire to (somehow... compiler switch?) have RQA be “on by default” (ie: opt-out rather than opt-in). I’m not suggesting we explore that. But, yeah, keeping firm on RQA is important.
I'm in favour of option 3. I originally suggested the second option in the reddit discussion, but the explanation of why that might be undesirable made sense so I instead suggested adding an additional relaxed RQA attribute, and I still think it's the best option because of the opt-in nature of it.
My reasoning is that, with the introduction of local opens, the binary "RQA or not" choice currently available will not be nuanced enough. F#'s documentation suggests that RQA be used to avoid name conflicts and improve maintainability; with that in mind, there are cases, like the collection modules, where RQA makes sense now because it blocks conflicts, but in an F# with local opens, the all-or-nothing approach is needlessly inflexible. You won't want to do a full open
of List and clobber anything else that has a map
or append
function, but doing so is perfectly logical in a constrained scope, such as a function you write to manipulate a list, so it would make sense to give the List module the new RelaxedQualifiedAccess (or whatever, I suck at naming things) attribute instead.
Adding a new attribute and moving some of these modules to it would make local opens more widely (and immediately) useful without violating the spirit of the "avoid conflicts and improve maintainability" logic by allowing these limited opens in places it makes sense but is currently not possible, like collections modules. There's a lot of naming overlap in collection modules, so the current RQA restriction makes sense from that perspective, but in an F# that allows limited opens it would be beneficial to allow them to be used there without fully opening them up. It's a perfect example of why you don't want to remove RQA completely but could benefit from a switch to a less restrictive policy.
I’m not keen on weakening the effectiveness RQA.
@pblasucci: Option 3 wouldn't be weakening RQA. Since this new behaviour would be opt-in (via a new attribute), RQA itself would remain unchanged so that nobody would be surprised down the road by their library having looser constraints than they used to have through no action of their own, but libraries that can benefit from the change could be swapped to the new relaxed attribute at their own pace. Existing code not using local opens would see no change, and people not interested in using local opens would see no difference in this relaxed RQA vs. standard RQA so changes to some modules (like List) would not have any effect on them either.
The whole thing becomes opt-in this way: you choose to use local opens, and you choose to allow them. RQA retains its strictness and modules using the relaxed attribute will act like RQA for existing code that isn't using local opens.
If by local open we mean open List
, then I'm for option 1 as I don't think there should be any behavioural difference between a local open and a non-local one. If locally opening a module makes sense, you rely on user discipline to not over-extend the local scope, so you might as well not make it RQA in the first place.
But if an inline open syntax were to be considered too, like OCaml's List.( ... )
, that one should probably ignore RQA, as the main use case is removing the boilerplate of module labels in one liners.
Arguably, this is all clearer in OCaml where you can write inner modules for the few functions that make sense to (locally) open and then include
them in their parent modules, which you're discouraged to open. If such a pattern were to catch on in F#, we wouldn't need to ignore RQA for any syntax, but since F# lacks include
, library authors would have the extra burden of exporting all members a second time.
Just my two cents: I like the Rust/Ocaml style local opens, but I also really like RQA.
Yes, it complicates the language, but the code is many times more readable, especially for more complex expressions.
My thinking is that since most of us use some sort of IDE, or vim/emacs+ionide. Most of the disambiguating and questions of "where does this function/type come from?" are answered with a simple hover-over.
Submitted by Gauthier Segay on 4/12/2016 12:00:00 AM
79 votes on UserVoice prior to migration
It would be nice to be able to use open in function scope and maybe other places where it currently is illegal.
e.g. see also https://github.com/fsharp/fslang-suggestions/issues/227
Original UserVoice Submission Archived Uservoice Comments