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

Generalize over mut and non-mut items #976

Closed

Conversation

Projects
None yet
10 participants
@tommit
Copy link

tommit commented Mar 14, 2015

@tommit

This comment has been minimized.

Copy link
Author

tommit commented Mar 14, 2015

For better readability and to retain more of the proper CamelCase and snake_case looks for traits, structs and functions, we could change them from being the prefix opt- and mut- to being the postfix -opt and -mut respectively.

@mahkoh

This comment has been minimized.

Copy link
Contributor

mahkoh commented Mar 14, 2015

👍 Maybe not this syntax, but being generic over mutability is often useful. I believe that all Deref and DerefMut implementations in the rust repo are exactly the same except that the mut one has mut in it.

@tommit

This comment has been minimized.

Copy link
Author

tommit commented Mar 14, 2015

@mahkoh I thought about putting opt in template parameters, right after lifetime parameters and before type parameters, and opt would implicitly default to being "nothing" (instead of to being mut). That still leaves us with the question of how to rename the mut versions of methods and free functions so that they're different from the non-mut versions.

where T: opt-Trait
{
fn opt-other_foo(&mut self) {
let t = self.opt-bar();

This comment has been minimized.

@reem

reem Mar 14, 2015

this syntax is ambiguous between self.opt - bar() and self.opt-bar()

This comment has been minimized.

@tommit

tommit Mar 14, 2015

Author

@reem Who do you mean it's ambiguous to?

  • The parser
  • The compiler
  • Programmer who is reading quickly and doesn't have syntax highlighting

It shouldn't be ambiguous to the first two, because the opt keyword followed by one or more white space followed by - would always be a compile-time error.

This comment has been minimized.

@reem

reem Mar 14, 2015

I meant the parser, but I guess it's not ambiguous if you ban the ambiguity ;)

This comment has been minimized.

@jmesmon

jmesmon Mar 15, 2015

On the use of - note: allowing - in identifiers is one of the alternatives in #940 (though I can't say many people seem interested).

@reem

This comment has been minimized.

Copy link

reem commented Mar 14, 2015

Being generic over mutability is definitely useful, but I think a lot of careful thought is necessary before we settle on any specific incarnation; there are both many conceptual consistency and practical issues that need to be considered.

Unfortunately, I'm not convinced this proposal is up to dealing with the magnitude of this challenge. For instance, it doesn't cover abstracting over the "by-value" mode at all, meaning it can't be used to collapse the Fn traits due to FnOnce and isn't clearly extensible for future reference types like &own or even existing ones like Box, which is needed to collapse FnBox/Invoke.

@lfairy

This comment has been minimized.

Copy link
Contributor

lfairy commented Mar 15, 2015

I agree with @reem in that it needs much more thought. Such extensions tend to have tricky edge cases which aren't apparent from a few simple examples.

I also don't see a mention of HKT, which may solve the same problem in a more general way.

@blaenk

This comment has been minimized.

Copy link
Contributor

blaenk commented Mar 16, 2015

I would really love to have this functionality, but not in the proposed form. I completely agree with @reem and @lfairy, especially regarding HKT and a potentially more general solution.

@Ericson2314

This comment has been minimized.

Copy link
Contributor

Ericson2314 commented Mar 19, 2015

Yeah what we want to do is parametrize the capabilities of pointer types. This proposal suffers from the similar expressiveness issues as Any -> Any w.r.t ∀a. a -> a. I hear the Disciple language (http://trac.ouroborus.net/ddc/) has done some interesting work with parametric regions and effects; I recommend anybody wanting to take up this problem consult their work first. To be clear, the problem is very real and fixing it will allow us to consolidate various APIs quite dramatically, but sadly this proposal isn't up to the task.

@glaebhoerl

This comment has been minimized.

Copy link
Contributor

glaebhoerl commented Mar 19, 2015

Another source of inspiration might be this presentation and the many references mentioned in it. In particular I wonder if we couldn't think of our reference types as being a combination of a pointer plus the capability to access it, after a similar fashion as suggested therein:

struct Ptr<'a> { addr: usize } // on its own, can't be read nor written
impl<'a> Copy for Ptr<'a>

struct SharedAccessCap<'a, T>;
impl<'a, T> Copy for SharedAccesssCap<'a, T>

struct MutAccessCap<'a, T>;
// no Copy

intrinsic fn read_shared<'a, T>(ptr: Ptr<'a>, cap: SharedAccessCap<'a, T>) -> T
intrinsic fn read_mut<'a, T>(ptr: Ptr<'a>, cap: MutAccessCap<'a, T>) -> (T, MutAccessCap<'a, T>)
intrinsic fn write_mut<'a, T>(ptr: Ptr<'a>, cap: MutAccessCap<'a, T>, val: T) -> MutAccessCap<'a, T>
intrinsic fn downgrade<'a, T>(cap: MutAccessCap<'a, T>) -> SharedAccessCap<'a, T>

type &'a     T = (Ptr<'a>, SharedAccessCap<'a, T>)
type &'a mut T = (Ptr<'a>, MutAccessCap<'a, T>)

...or something like that.

I suspect this couldn't really work as-is, because our lifetimes aren't quite the same thing as their regions (I believe it's possible for two disjoint pieces of memory to have the same lifetime?), but perhaps the idea could still be useful somehow. (I'm an expert on neither.)

(It gets harder if you also wish to consider &move and &out - but then again, code which could be generic over two or more reference types out of which at least one is &move or &out seems like it would be uncommon.)

@Ericson2314

This comment has been minimized.

Copy link
Contributor

Ericson2314 commented Mar 19, 2015

@glaebhoerl cool slides! Also I'd guess that code that partially assigns + refines a pointer can be generic over various combinations of &mut, &out, and &in.

@brson brson self-assigned this Mar 19, 2015

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Mar 21, 2015

There's no question that there are many types that could usefully generalize over mut and shared. However, I think the best answer here is not to jump to a special-purpose kind of generalization, but rather to lean on type generalization, perhaps augmented with HKT in the future. After all, writing code is generic over mut/non-mut is possible today, though not fully general, by using type parameters:

struct Foo<R: Deref> {
    reference: R
}

this struct can be used with &T or &mut T and -- even better -- with Box<T> and Rc<T> too. (Here T could be written R::Output.)

@brson

This comment has been minimized.

Copy link
Contributor

brson commented Apr 6, 2015

I'm also concerned about the unknowns here and think this duality is something we've lived with for a long time and can continue to do so. As @nikomatsakis there are potential other solutions. Let's not be hasty.

Thanks for the thought-provoking RFC, but we're not going to pursue this avenue.

@brson brson closed this Apr 6, 2015

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