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

Conversation

@aturon
Copy link
Member

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

This comment has been minimized.

Copy link
Contributor

commented Feb 20, 2015

This is NICE! 👍 👏

@Valloric

This comment has been minimized.

Copy link

commented Feb 20, 2015

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link

commented Feb 21, 2015

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

@pyfisch

This comment has been minimized.

Copy link
Contributor

commented Feb 21, 2015

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

@mahkoh

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Contributor

commented Mar 6, 2015

@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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Contributor

commented Mar 13, 2015

This is great!

@blaenk

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Member Author

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

This comment has been minimized.

Copy link
Contributor

commented Mar 17, 2015

Alright that's fine!

@thepowersgang

This comment has been minimized.

Copy link
Contributor

commented Apr 8, 2015

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)

@lfairy

This comment has been minimized.

Copy link
Contributor

commented Apr 24, 2015

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.

@lfairy

This comment has been minimized.

Copy link

commented on text/0000-custom-preludes.md in e2ae1bd Apr 24, 2015

"exmaple" should be "example" ;)

@aturon aturon added the T-lang label May 22, 2015

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

commented Jun 10, 2015

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

@brson

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Contributor

commented Jun 10, 2015

:(

On Wednesday, June 10, 2015, Brian Anderson notifications@github.com
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

This comment has been minimized.

Copy link
Member Author

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

This comment has been minimized.

Copy link
Contributor

commented Aug 7, 2015

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

This comment has been minimized.

Copy link
Contributor

commented Aug 7, 2015

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

#[prelude]
mod prelude {
// std actually provides multiple preludes now
pub use std::prelude::v1::*;

This comment has been minimized.

Copy link
@RalfJung

RalfJung Aug 7, 2015

Member

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

This comment has been minimized.

Copy link

commented Aug 7, 2015

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

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link

commented Aug 8, 2015

👍 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

This comment has been minimized.

Copy link
Member

commented Aug 10, 2015

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Contributor

commented Aug 14, 2015

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.

@alexcrichton alexcrichton referenced this pull request Nov 11, 2015

Closed

Tracking issue for libcore + no_std stabilization #27701

4 of 4 tasks complete
@hauleth

This comment has been minimized.

Copy link

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].

@canndrew canndrew referenced this pull request Oct 31, 2017

Merged

Fix clippy warnings #878

@TimDiekmann

This comment has been minimized.

Copy link

commented Mar 2, 2018

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

@Restioson

This comment has been minimized.

Copy link

commented May 25, 2018

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

@Restioson Restioson referenced this pull request May 25, 2018

Open

No std? #240

@joshtriplett

This comment has been minimized.

Copy link
Member

commented Jun 27, 2018

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.