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

RFC: Custom preludes #890

Closed
wants to merge 1 commit into from
Closed

Conversation

aturon
Copy link
Member

@aturon aturon commented Feb 20, 2015

Add a #[prelude] attribute that can be applied to a single module at
crate root to serve as a custom prelude for that crate.

Thanks to @XMPPwocky for helping develop and prototype these ideas!

Rendered

@aturon aturon changed the title RFCustom preludes RFC: Custom preludes Feb 20, 2015
@blaenk
Copy link
Contributor

blaenk commented Feb 20, 2015

This is NICE! 👍 👏

@Valloric
Copy link

I love how it democratizes preludes; it should also help a lot for embedded Rust use-cases.

Unfortunately I think this is one of those ideas that looks great on paper but ends up causing headaches in practice.

This has the potential of creating a project/company level language "fork," in the sense that the top-level names that one would expect to be present could be entirely different from project to project. At Google, we have a very large, unified C++ codebase where many names from std:: are lifted to the top-level scope. This decision was made a long time ago and it's been causing enough problems that it's being slowly rolled back (at non-trivial engineering cost).

You basically end up with a custom language dialect that's slightly different, but different enough. New developers won't know it, they'll hate having to learn it and any code that's been written for the "standard" dialect can easily fail to compile in yours.

I'd advise extreme caution here. I've seen the consequences of a custom C++ "prelude" in a large codebase and it's widely considered to be a mistake.

DISCLAIMER: My opinions are mine and mine alone; I speak only for myself, not my employer.

@pyfisch
Copy link
Contributor

pyfisch commented Feb 21, 2015

This still allows only the use of one prelude. But what if I need the IO "prelude" and the science math lib "prelude" in an application. Here I can't use this prelude property, right? So I would prefer using standard imports as used today. Rust already has really many features, and I do not think that it needs this kind of syndactic sugar.

@utkarshkukreti
Copy link

@pyfisch isn't that exactly what the Application example section shows how to do?

@pyfisch
Copy link
Contributor

pyfisch commented Feb 21, 2015

I see it. So you can create a custom prelude for your application. Ok, looks reasonable.

@mahkoh
Copy link
Contributor

mahkoh commented Mar 6, 2015

+1 I need this.

I have a trait with a method read that I use in many modules. In all of these modules I have to disable the prelude and manually import many things such as Ok, Option, Err, Some.

@glaebhoerl
Copy link
Contributor

@mahkoh Another way to make that less painful would be hiding imports a la Haskell:

import MyModule (read)
import Prelude hiding (read)

(Still boilerplate, but not as much - not for each item you want from the prelude.)

@blaenk
Copy link
Contributor

blaenk commented Mar 9, 2015

Yeah, one of the first things I looked for with Rust modules was Haskell's hiding. I think it'd be nice to have, to explicitly state which imports are taking over instead of implicitly shadowing. They'd go well with glob imports too, use mod::* hiding {one, two} (or whatever syntax).

@wycats
Copy link
Contributor

wycats commented Mar 13, 2015

This is great!

@blaenk
Copy link
Contributor

blaenk commented Mar 17, 2015

hey now that we're back from our RFC vacation how about RAMMING THIS THROUGH!! 💪 😁 👊

(or at least giving an update :)

I do think that hiding functionality would really complement this well, but it's not a huge priority in case the rest of you guys aren't on board with it, since it would only make more explicit some things that are already possible now (yet implicit; shadowing). We'll also want to have some way to refer to the 'current prelude' so we can specifically hide things from it, so at least both of these forms:

// glob import from some::module _except_ for one, two, and three
// this makes it clear that we're _definitely_ not using the `one`, `two`, or `three`
// from some::module
// if we see those items in the code then they're
// most likely from the current or other module
use some:module::* hiding {one, two, three};

// don't import one, two, three from the 'currently set' prelude
// would probably make prelude at least a context-dependent keyword though :/
// this is just an example syntax
use prelude hiding {one, two, three};

// another alternative would probably be
hiding {one, two, three};

// it also lets you glob import modules that would otherwise clash
// due to their contents, this way you can simply exclude those items that clash
// assume first and second contain an item `common`
use first::* hiding {common};
use second::*;

Personally I think this explicitness is what I expect from Rust, and I think it should at least help with some of the concerns about making it difficult to tell what comes from where.

@aturon
Copy link
Member Author

aturon commented Mar 17, 2015

@blaenk :) I'll raise this RFC at the weekly meeting today, just to garner more feedback from the team. It's been a bit on the backburner, since it's not really a 1.0 back-compat priority.

@blaenk
Copy link
Contributor

blaenk commented Mar 17, 2015

Alright that's fine!

@thepowersgang
Copy link
Contributor

An alternative to putting an annotation on a module is to use a crate attribute instead, which would presumably be a little more visible, and more obvious that it's a per-crate option. The example I used in #743 was #![prelude="core::prelude"], but this format might have parsing issues (use of :: within the string)

@lambda-fairy
Copy link
Contributor

Should we allow tagging use and extern crate items directly as well?

For example, if I publish an alternative prelude called awesome_prelude, I should be able to write this:

#[prelude] extern crate awesome_prelude;

instead of this:

#[prelude]
mod prelude {
    extern crate awesome_prelude;
    pub use awesome_prelude::*;
}

This pattern may be useful for larger frameworks, with many component crates sharing a common core.

@aturon aturon added the T-lang Relevant to the language team, which will review and decide on the RFC. label May 22, 2015
@nikomatsakis
Copy link
Contributor

@aturon do you feel we are ready to make a decision here?

@brson
Copy link
Contributor

brson commented Jun 10, 2015

Since this has sat around for so long without movement, I'd suggest that is an indication it is not a pressing matter and we can punt it further.

@blaenk
Copy link
Contributor

blaenk commented Jun 10, 2015

:(

On Wednesday, June 10, 2015, Brian Anderson [email protected]
wrote:

Since this has sat around for so long without movement, I'd suggest that
is an indication it is not a pressing matter and we can punt it further.


Reply to this email directly or view it on GitHub
#890 (comment).

  • Jorge Israel Peña

@aturon
Copy link
Member Author

aturon commented Jun 10, 2015

Since this has sat around for so long without movement, I'd suggest that is an indication it is not a pressing matter and we can punt it further.

FWIW, I ended up not pushing on this largely due to the concerns that @Valloric raised (and the 1.0 push). While this RFC would have some serious ergonomic benefits for external crates, it does carry some hard-to-quantify risks about fragmenting the language into "prelude dialects".

It'd be useful to gather some evidence, in the form of painful repeated use blocks, on the other side of this equation.

@thepowersgang
Copy link
Contributor

Raising again now that #1184 has been applied.

What is the reasoning for using a module attribute over a crate attribute? I would expect a crate-level attribute nominating a prelude path would be easier for users to locate and the compiler to handle than a magic marker on the prelude module.

@aturon Could you add the following to the RFC as an alternative?
#![prelude="path::to::my::prelude"]

@nikomatsakis
Copy link
Contributor

Hear ye, hear ye. This RFC is moving to final comment period.

@nikomatsakis nikomatsakis added the final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. label Aug 7, 2015
#[prelude]
mod prelude {
// std actually provides multiple preludes now
pub use std::prelude::v1::*;
Copy link
Member

Choose a reason for hiding this comment

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

So to use this, I have to figure out what the current "default" prelude is, somehow? I think in the absence of no_std, the default prelude should still be included, with this prelude being added.

@larsbergstrom
Copy link

Apologies if a dumb question, but in another life, I've seen problems with this and syntax-expansion style macros. That is, assume there's a macro in the standard prelude, and it expands into code that references other prelude types. While you can run into problems with that by shadowing today (e.g., https://play.rust-lang.org/?gist=baeb5e0af8c23be403b2&version=stable ), right now it would be pretty obvious because you either have a glob import or a shadowing definition of the type within the same module.

With custom preludes, things are a little more complicated, as it could lead to some pretty wonky errors that require non-local reasoning to puzzle through. At the very least, a couple of options are:

  • Warning if additional prelude members shadow prelude members from previous glob imports
  • Requiring that all macros exposed in prelude modules expand to fully-qualified identifiers rather than other prelude members

@RalfJung
Copy link
Member

RalfJung commented Aug 7, 2015

@larsbergstrom The right solution to this issue is macro hygiene: Names in macros should be resolved in the context the macro was defined in, not in the context the macro is used in. I don't know what the current status of macro hygiene in Rust is, though.

@DanielKeep
Copy link

👍 I think the restriction to a root module is enough that, once you're aware custom preludes exist, it shouldn't cause any major problems in tracking down symbols.

@alexcrichton
Copy link
Member

I, like @aturon, agree with @Valloric's original thoughts and feel the same as @brson. I haven't felt the need for this in any projects recently and I feel like we may want to get more traction with what we've got today before we dive into custom preludes.

@bvssvni
Copy link

bvssvni commented Aug 11, 2015

In most cases I would like to have this feature is when:

  • Importing some extra stuff from the standard library used many places, like Rc
  • Access the public items from everywhere in the create
  • A few commonly used types from large external crates
  • Many small external crates with types used everywhere
  • Deep dependency paths in the ecosystem and risk for namespace collisions

Today some of the ergonomic issues are solved by reexporting, which increases risk of namespace collisions. This could be better by organizing a custom prelude per application.

I have thought a bit of how this would affect navigating larger libraries, and I believe it could solve some pain points:

  1. Some people use a all, others use traits or prelude, it is not very consistent
  2. From what I know about the external API, it can be hard to keep track of relative imported items, since they might be renamed or use a different relative paths

Other things:

  • The concern about custom Rust dialects is understandable, but there are also use cases where you design for a particular domain, so I don't view it as a strictly negative thing.
  • From a refactoring perspective this is a huge win, as you don't have to dig through the error messages from the compiler.

Overall it is a +1 for me.

@nikomatsakis
Copy link
Contributor

At the language subteam meeting, we decided to close this RFC pending stronger motivation. This might be a good addition to the language, but there doesn't seem to be urgent need at this time, and it's better to give things time to evolve and focus on other priorities.

@hauleth
Copy link

hauleth commented Dec 7, 2015

I would only add 1 thing. Loading custom prelude should be disabled by default and there should be additional option #[use_prelude] that would be similar to #[use_macro].

@TimDiekmann
Copy link
Member

2 years have passed. May we should consider reopen this again?

@Restioson
Copy link

👍 100%, a tad annoying to be using alloc and not have the normal std prelude things.

@joshtriplett
Copy link
Member

I would prefer to leave this closed. If you want a custom prelude, you can easily create a prelude.rs and use ::prelude::*; in individual modules. That makes it easier to just look up at the top of a crate to resolve a name.

@eonil
Copy link

eonil commented Dec 4, 2019

I like this because std doesn't cover all programs. Some programs cannot allocate heap. Some programs should prevent all I/O.

Implicit prelude to std is not welcome in such programs. It would be nice if this feature designed to replace default prelude from std to something else.

In that case, I think this would be better to be a "crate-wide" feature.

For example, this imports symbols in prelude module from your_std crate for all source files instead of ::std::prelude.

#![default_prelude(your_std)]    

Or by compiler flag.

rustc --default-prelude=your_std

Or by Cargo configuration.

default_prelude = "your_std"

If you want to import multiple symbols from multiple crates, you need to build your own "aggregation crate". That would be better than making feature more complicated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.