Re: [rust-dev] "intimidation factor" vs target audience

2013-01-22 Thread Gábor Lehel
On Tue, Jan 22, 2013 at 8:01 PM, Dean Thompson
 wrote:
> FWIW, Niko's ${foo}bar notation helps my mental "parser" a great deal,
> because it
> makes foo look like a modifier to me. When I see &foo/bar, my mind fights
> to make
> it a pointer to foo with a strange trailing bar.

+1 to this. I haven't been able to articulate why &foo/bar feels
unnatural to me, but this is probably it. It's hard to intuit by
looking at it which half is what, and why it is that way instead of
the reverse. That, and my brain tries to interpret it as division, or
something for which division is a good metaphor, and it doesn't lead
anywhere.
(I don't mean to harp on the issue, I already commented about it at
Niko's blog.)

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] "intimidation factor" vs target audience

2013-01-26 Thread Gábor Lehel
I've since seen multiple people express that &{foo} ("option 8") is
the most appealing to them (which may or may not be confirmation bias,
given that it's also the most appealing to me). In either case, one
argument in favor of that route might be that lifetimes are (probably
by far) the most alien and intimidating part of the language, and
anything to make them more accessible is worth pursuing. So *if* there
is a consensus that "option 8" is in fact the nicest option, then it
might be worth biting the bullet. If it were any other part of the
language, I'm not sure, but here if whitespace dependency is the
price, it might be worth the sacrifice.

(That said, I agree that &'foo is also better than the current syntax.)

On Wed, Jan 23, 2013 at 4:39 AM, Benjamin Striegel
 wrote:
> Sadly, you should really read this subsequent blog post:
>
> http://smallcultfollowing.com/babysteps/blog/2013/01/15/lifetime-notation-redux/
>
> It turns out that this syntax is ambiguous without introducing a whitespace
> dependency. I think it might still be worth it, but I know that a lot of
> people tend to shy away from such things on principle.
>
>
> On Tue, Jan 22, 2013 at 2:01 PM, Dean Thompson 
> wrote:
>>
>> Looking at Niko's blog post
>>
>>
>> http://smallcultfollowing.com/babysteps/blog/2012/12/30/lifetime-notation/
>>
>> We do, to my eye, get a huge improvement if we both tweak the notation and
>> also augment the ref deconstruction syntax to indicate the resulting
>> pointer
>> timeline.
>>
>> Doing this with Niko's preferred option 8 gives us:
>>
>> pure fn each(&self, f: fn(&(&{self}K, &{self}V)) -> bool) {
>> match *self {
>>   Leaf => (),
>>   Tree(_, ref{self} left, ref{self} key,
>>ref{self} maybe_value, ref{self} right) => {
>> left.each(f);
>> match *maybe_value {
>> Some(ref{self} value) => {
>> f(&(key, value));
>> }
>> None => ()
>> };
>> right.each(f);
>> }
>> }
>>
>>
>> FWIW, Niko's ${foo}bar notation helps my mental "parser" a great deal,
>> because it
>> makes foo look like a modifier to me. When I see &foo/bar, my mind fights
>> to make
>> it a pointer to foo with a strange trailing bar.
>>
>> Dean
>>
>>
>> On 1/22/13 9:23 AM, "Graydon Hoare"  wrote:
>>
>> >On 22/01/2013 6:55 AM, Dean Thompson wrote:
>> >
>> >> I'm looking at some code that Niko Matsakis updated in
>> >> https://github.com/stevej/rustled/commits/master/red_black_tree.rs
>> >>
>> >>   pure fn each(&self, f: fn(&(&self/K, &self/V)) -> bool) {
>> >> match *self {
>> >>   Leaf => (),
>> >>   Tree(_, ref left, ref key, ref maybe_value, ref right) => {
>> >> let left: &self/@RBMap = left;
>> >> let key: &self/K = key;
>> >> let maybe_value: &self/Option = maybe_value;
>> >> let right: &self/@RBMap = right;
>> >> left.each(f);
>> >> match *maybe_value {
>> >> Some(ref value) => {
>> >> let value: &self/V = value;
>> >> f(&(key, value));
>> >> }
>> >> None => ()
>> >> };
>> >> right.each(f);
>> >>   }
>> >> }
>> >>   }
>> >>
>> >> I understand this code reasonably well. I greatly value the attention
>> >> to safety in Rust, and I appreciate the value of pointer lifetimes in
>> >> maintaining that safety.
>> >>
>> >> My gut reaction, though, is that this code is almost as intimidating
>> >> as Haskell. Even more worrisome to me, I think most mainstream
>> >> programmers would find the *explanation* of this code intimidating.
>> >
>> >I agree that the cognitive load on this code sample is high. This is the
>> >main risk we took (aside from "potential unsoundness", which I didn't
>> >really think to be a big risk, judging from Niko's comfort with the
>> >semantics) when adopting first class region pointers: that the resulting
>> >types would be too complex to understand, and/or require too much
>> >chatter when writing out in full.
>> >
>> >To my eyes the matter is not yet entirely clear. It's complex but it's
>> >not quite "impossibly complex"; if you made all the '&self/' symbols
>> >into just '&' it would be, I think, not so bad. Compare if you like to
>> >the associated bits of code from libc++ required to implement
>> >roughly-equivalent "iterate through the treemap" sort of functionality:
>> >
>> >
>> >_LIBCPP_INLINE_VISIBILITY
>> >__tree_iterator& operator++() {
>> >__ptr_ = static_cast<__node_pointer(
>> >__tree_next(
>> >static_cast<__node_base_pointer>(__ptr_)));
>> >return *this;
>> >}
>> >
>> >template 
>> >_NodePtr
>> >__tree_next(_NodePtr __x) _NOEXCEPT
>> >{
>> >if (__x->__right_ != nullptr)
>> >return __tree_min(__x->__right_);
>> >while (!__tree_is_left_child(__x))
>> >__x = __x->__parent_;
>> >return __x->__parent_;
>> >}
>> >
>> >template 
>> >inline _LI

Re: [rust-dev] Lifetime notation

2013-01-31 Thread Gábor Lehel
(Not sure if anyone cares about my opinion, but: if apostrophes are a
given, the braces of "option 8" aren't obviously preferable to me any
more. The appeal of option 8 was that it visually distinguished
lifetime parameters, and just overall looked nice, gave the right
impression. With apostrophes the lifetimes are already distinguished
by the apostrophes, and it no longer looks as nice. It's not clearly
better to me than angle brackets with a consolidated lifetime + type
parameter list. So if I had a vote, I would probably cast it for the
latter, because at least it's simpler.)

On Thu, Jan 31, 2013 at 1:54 PM, Sanghyeon Seo  wrote:
> UtherII on Reddit /r/rust suggested an idea I like:
>
> &{'lt} T
> T{'lt}
>
> Basically "option 8" of 
> http://smallcultfollowing.com/babysteps/blog/2012/12/30/lifetime-notation/
> with ' from 
> https://mail.mozilla.org/pipermail/rust-dev/2013-January/002942.html
>
> This does need a lookahead but as far as I can tell unambiguous and 
> manageable. More on:
> http://www.reddit.com/r/rust/comments/17ka3b/meeting_weekly_20130129_region_syntax_impl_type/c86t7wg
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] lifetime syntax

2013-02-13 Thread Gábor Lehel
On Wed, Feb 6, 2013 at 7:52 PM, Graydon Hoare  wrote:
> On 13-02-06 10:33 AM, Isaac Aggrey wrote:
>> I realize the Rust community is beginning to settle on the `'lt` as the 
>> syntax
>> to rule them all, but is using any sort of suffix for lifetime syntax a
>> non-starter?
>
> Yeah, sorry, this horse* is dead.
>
> -Graydon
>
> (* no actual horses were harmed in the discussion of this feature)
>

Apologies, I've just spotted another horse whose lifetime I couldn't
quite infer, though it is probably dead:

What about just having types be uppercase and lifetimes be lowercase?
I was reading the weekly meeting, specifically the example ~'task
Reader, when I thought "why not just ~task Reader -- oh, right,
lifetimes need the sigil", which lead me to the present horse. Unless
my memory has been corrupted, in every example I've seen so far,
probably not by conscious design but also not by accident, the types
have been uppercase and the lifetimes lowercase. Why not codify it? I
don't know if there are serious philosophical agreements about the
idea of case being significant. But in any case, it would eliminate a
lot of punctuation, and I think code would be a lot more pleasant to
look at.

...I was wrong, types are not all uppercase, the primitive ones like
bool, int, etc. are not. That probably doesn't help the prognosis. But
if lifetime parameters need to be explicitly introduced, maybe it
doesn't lead to actual ambiguity? If it was introduced as a parameter
then lowercase -> lifetime, uppercase -> type, otherwise if it's a
predefined lifetime name -> it's a lifetime, otherwise -> it's a type.
But maybe that wouldn't play well with the future-idea of labelling
blocks with lifetimes. Or the primitive type names could be special
cased, but I guess that's not appealing. Iunno.

Sorry again for bringing this up after the matter's already been decided.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Many little questions while writing a Vector class

2013-02-21 Thread Gábor Lehel
On Thu, Feb 21, 2013 at 3:17 PM, Niko Matsakis  wrote:
>
>
> Ashish Myles wrote:
>> 5. Consistency of len/size:
>>Each language seems to have its own standard for specifying container
>>size. I thought Rust's convention was len from vector, dvec, list, etc,
>>but deque uses size.  Should that also be len?  Or rather, as "size" is
>>the more generic term (applies semantically to arbitrary containers
>> like
>>trees and maps), should "size" be the standard? Or, like ruby, perhaps
>>both could be allowed.
>
> Good question.  This is the sort of thing that we are going to be addressing
> as we cleanup the libraries.  I agree we should settle on one.  I don't
> personally care which it is. I'm personally not bothered by the semantic
> mismatch of a set having a length, but I guess `size()` would be the most
> broadly applicable and least likely to inspire later flame wars.

FWIW Qt uses `count()`. (but also has size() for STL compatibility,
and length() as well when it makes sense...)


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Many little questions while writing a Vector class

2013-02-21 Thread Gábor Lehel
On Thu, Feb 21, 2013 at 6:37 PM, Niko Matsakis  wrote:
> There is some design work needed here.  In general, this seems to be related
> to the desire for associated types [2].

Coming from Haskell, they seem like separate things to me (or rather
they're related only by them being extensions to the trait system). On
the one hand there's the desire to have types/traits parameterized on
things other than types: non-type template parameters in C++ parlance,
or type-level literals in Haskell (or type-level things of kind other
than * or k -> k, in a broader sense). And on the other hand there's
the desire to have constants as members of a trait (type class).
That's Haskell 98, no extensions required. E.g. you have `class
Bounded a where minBound :: a; maxBound :: a`. (Haskell doesn't have a
distinction between nullary functions and simple values, so there's no
difference there between `static fn len() -> uint` and `const len:
uint`.) But this is just a mapping from types to values, which is what
type classes do. Associated types come into the picture when you want
to map types to types.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] library editing

2013-03-01 Thread Gábor Lehel
On Thu, Feb 28, 2013 at 8:46 PM, Graydon Hoare  wrote:
> On 28/02/2013 10:50 AM, Graydon Hoare wrote:
>
>> get away with doing this (in libstd):
>>
>> #[vers="1.0.0"]
>> pub mod foo = "github.com/user/foo#1.0";
>>
>> #[vers="1.1.0"]
>> pub mod foo = "github.com/user/foo#2.0";
>
> Also note: in case this sounds too terrifying in terms of hacking a new
> sub-dimension onto resolve, not to mention figuring out how to
> (automatically?) transform minor-version bumps in the 1.x and 2.x series
> of libfoo into corresponding minor-version bumps in libstd, this could
> equally be accomplished manually / by convention, in terms of:
>
>  pub mod foo_v1 = "github.com/user/foo#1.0";
>  pub mod foo_v2 = "github.com/user/foo#2.0";
>  pub mod foo {
>  pub use foo_v1::*;
>  }
>
> or even possibly
>
>  pub mod foo {
>  pub mod v1 = "github.com/user/foo#1.0";
>  pub mod v2 = "github.com/user/foo#2.0";
>  pub use foo::v1::*;
>  }
>
> assuming foo doesn't define names v1 and v2 itself (unlikely). Our
> module system is quite strong when it comes to rebinding names. Doing it
> this way just involves more legwork. But it might be desirable from a
> "minimizing cognitive load" perspective to make this explicit in the
> code and avoid having to reason about versions when thinking about what
> resolve is doing.
>
> -Graydon

This sounds a lot like what C++ is doing with inline namespaces:
http://stackoverflow.com/a/11018418


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] const, static, global, mut, syntax!

2013-03-12 Thread Gábor Lehel
Bikeshed wh!

The connotations/meanings I associate to these words are:

const => constant, doesn't change / can't be changed
static => statically known (usually "without running it", like in static
types vs. dynamic types, static analysis, etc.)

The desired meaning here seems to be "statically allocated". So I would go
with static. Close enough! The equivalent in Rust for const as above is
basically "not mut". As long as globals were forced to be immutable
(constant) the two meanings overlapped, so there wasn't any glaring problem
(maybe some subtle redundancy), but once you let them be mut the conflict
is obvious.


On Tue, Mar 12, 2013 at 8:44 PM, Graydon Hoare  wrote:

> Hi,
>
> At today's meeting we had a a discussion about two issues that
> intersect, oddly, at a particular point of the syntax. The two issues are:
>
>   - Removing the 'static lifetime-name -- currently used to denote
> the lifetime of consts -- since the only other use of the word
> (static fn ...) is going away when we finish with explicit-self;
> renaming it (probably) to 'const since that's the word used to
> introduce consts!
>
>   - Adding mutable globals (unsafe-to-access, but needed in some
> corner cases for mutexes and such). This is an old bug
> (two years! https://github.com/mozilla/rust/issues/553 ) that
> we've long assumed we'd have to implement eventually.
>
> We agreed to do the former (rename 'static to 'const) and collapse all
> uses of the word "const" to mean this "read-only-memory" thing. But then
> when pushing around possible syntaxes for mutable globals, such as the
> following not-so-charming options:
>
>   let mut x : T = v;// like in functions, but different since
>the RHS must be a const and can refer to
>values declared-after it.
>
>   const mut x : T = v;  // adding a modifier to 'const' but one that
>reads somewhat absurdly: "constant mutable"?
>
>   unsafe mut x : T = v; // more random choices...
>
> We stumbled on the _opposite_ possibility for const-vs-static: maybe we
> should be retiring the "const" keyword and standardizing on "static",
> for both mutable and immutable. The following cases would then fall out:
>
>   static x : T = v;  // constant init, immutable, like const today
>
>   static mut x : T = v;  // constant init, mutable, unsafe to access
>
> With values in that scope having 'static lifetime, and the word "const"
> no longer appearing anywhere in the language.
>
> There are other options, probably, that we did not consider. Some people
> suggested putting "global" in there instead of "static". Some suggested
> going back to "var" rather than "let".
>
> This is an intentional (brief) request for a little syntax
> bikeshed-discussion to see if anything else occurs to anyone, or if
> anyone has strong reactions / preferences / thoughts to any of the above
> options. We recognize that this does not _exactly_ align with the
> meanings of the highly-overloaded words "const" or "static" in C/C++,
> but think that there's some keyword and concept overlap here that can
> probably be minimized.
>
> (Maybe reply for only a couple days and we'll summarize opinions and
> pick something thereafter. No need for it to drag on forever.)
>
> Thanks,
>
> -Graydon
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Library Safety

2013-04-04 Thread Gábor Lehel
On Wed, Apr 3, 2013 at 5:26 PM, Dean Thompson wrote:

> The Rust team refers to this as an "effect system".  They originally had
> one, but that one proved unworkable and was deleted.  They continue to
> regard it as desirable but difficult to get right, and as a potential
> future.  Here's some 
> history.
> They would certainly welcome serious proposals or demos, although almost
> certainly continuing to hold it out for post-1.0. They would think in terms
> of first researching the most successful effect systems in other languages.
>
> Dean
>

Disciple is a strict-by-default Haskell dialect with region typing,
mutability in the type system, and effect typing, so maybe it could be one
place to look.

http://disciple.ouroborus.net/


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] getting rid of let a=3, b=4? (bikeshed alert)

2013-04-10 Thread Gábor Lehel
On Wed, Apr 10, 2013 at 3:37 PM, Chad Zawistowski wrote:

> In the case where both declarations are on the same line, I agree with the
> results of Niko's straw poll.
>
> let mut foo = 1, bar = 2;
>
> This should make both foo and bar be mut, in my opinion.
>
> Chad
>

The question is whether mut applies to let or to foo: (let mut) foo, bar
vs. let (mut foo, bar). Is there any other place left in the language
besides let, @, and & where mut is allowed? The latter cases seem to
support (let mut): if you have &mut foo, it's a pointer through which
mutation is allowed, named foo, in other words (&mut) foo, while the other
way, &(mut foo), doesn't make any sense: it's not foo that's mutable!
Similarly for @.

So yeah, +1 for existing interpretation.


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Library Safety

2013-04-16 Thread Gábor Lehel
On Sat, Apr 6, 2013 at 1:14 AM, Grant Husbands wrote:

> To some extent, yes. I'm not familiar enough with Rust's lint modes to say
> much more than that. I'm going to try using Rust for a while before giving
> a more detailed proposal.
>
> However, I will give a more concrete example, now that I know slightly
> more. In servo.rc (in the servo project), there's a line like this:
> extern mod stb_image;
>
> I want to be able to write it something like this:
> extern mod rust-jpeg ( nogc, safe );
>
> Doing so should ensure that the library cannot do GC or anything directly
> or indirectly unsafe (alternatively, it should import the version that was
> compiled that way). Then, the servo project can be sure that rust-jpeg
> cannot perform any unsafe operations (or GC), without manual audits of its
> imports or code. It essentially removes the JPEG library from the TCB
> (trusted computing base) of Servo. Carefully applied, it would make servo
> much more secure against maliciousness via supporting libraries.
>
> The important thing, to my mind, is that I don't have to audit the
> rust-jpeg library at all, and the worst it can do (probably) is a denial of
> service. If this became standard practice for Rust code, it would be a
> systems language in which it's feasible to easily include relatively
> untrusted third-party libraries, securely, and interact with them
> naturally. I think there's a lot of mileage in that.
>
> Grant.
>
>

SafeHaskell might be a relevant example of prior art here (at least
regarding the nothing-unsafe half of the above). Much like Rust, Haskell
has a strong static type system, but with escape hatches (unsafePerformIO,
unsafeCoerce, most things involving the FFI) which can subvert it if used
improperly or maliciously, as with unsafe { } in Rust.

The approach SafeHaskell takes is that safety is tracked at the module
level, with three classifications: Unsafe, meaning that the module exposes
an interface which can violate safety; Trustworthy, meaning that the module
imports Unsafe modules, but the author of the module asserts that they are
only used in ways that are safe, and that the module does not outwardly
expose any way to violate safety; and Safe, meaning that the module
transitively imports only Safe and Trustworthy modules. These can either be
declared by the module author, where in the case of Safe the compiler will
verify that it's true, or otherwise the compiler will infer Safe or Unsafe.

It's up to the system administrator to set policy regarding trusted
packages. Essentially, if a package is marked as trusted, Trustworthy
modules in the package will be treated as being Safe, otherwise they will
be treated as being Unsafe. If you try to compile something while requiring
it to be safe, and it imports Trustworthy modules from untrusted packages
(or of course actual Unsafe modules), the compiler will complain and refuse
to compile it.

Basically, using the term from your email, the "trusted computing base"
requiring human inspection would consist of the modules marked Trustworthy.


>
>
> On Thu, Apr 4, 2013 at 10:39 PM, Niko Matsakis  wrote:
>
>> It sounds to me like you're talking about something similar to the
>> current lint modes for GC etc?
>>
>> Niko
>>
>
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
>


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Update on I/O progress

2013-04-25 Thread Gábor Lehel
You write:

> So what actually happens if `new` encounters an error? To understand
> that it's important to know that what `new` returns is not a `File`
> but an `Option`. If the file does not open, and the condition
> is handled, then `new` will simply return `None`. Because there is an
> implementation of `Writer` (the trait required ultimately required for
> types to implement `write_line`) there is no need to inspect or unwrap
> the `Option` and we simply call `write_line` on it. If `new`
> returned a `None` then the followup call to `write_line` will also
> raise an error.

I'm an outsider, so apologies if I'm off the right track. But this sounds
wrong to me. Isn't the point of condition handlers to /handle the
condition/? Meaning resolve the problem, so that processing can continue?
Here as far as I can tell, they're only being used to say "fail
differently", which then leads to failing again the next time you try to do
something. Opening a file was actually one of the examples used to
illustrate the condition system way back when[1].

> * XXX: How should we use condition handlers that return values?

IMHO the right way would be to provide more fine-grained information about
the error (perhaps raising different conditions depending on the error,
instead of always the same condition and a giant enum describing it), and
use the return value of the condition to set policy wrt how to handle it
and/or to provide a substitute (as in the example). If there's nothing
reasonable that could be done, don't raise a condition, fail
unconditionally.

Whether to respond to failure by failing the task or by indicating it in
the return value seems like it would be better handled by having separate
functions for each. In the case where you expect success, the result
shouldn't be an Option. If the result is an Option, it shouldn't fail the
task (whether or not a condition handler is present). So for example:

open(path: &P, mode: FileMode, access: FileAccess) ->
FileStream

try_open(path: &P, mode: FileMode, access: FileAccess) ->
Option

If the expect-success version of a function returns (), the try_ version
would return bool rather than Option<()> (which are isomorphic).

Upon encountering an error, open() would raise a condition, and then
continue if possible, otherwise fail!().

I'm not sure whether try_open() would raise a condition or not. The way
conditions were described, if they aren't handled, the task fails.
try_open() definitely shouldn't fail. Would it be reasonable to raise a
condition and instead of failing, return None if it's not handled? If not,
then try_open() shouldn't raise any conditions, and should just return None.

The obvious drawback is twice as many functions, but it feels preferable to
forcing two different behaviours onto the same function. The fast
(expect-success) path would be just as clean as before, if not cleaner.

[1] https://mail.mozilla.org/pipermail/rust-dev/2012-October/002545.html
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Update on I/O progress

2013-04-25 Thread Gábor Lehel
On Thu, Apr 25, 2013 at 10:00 PM, Brian Anderson wrote:

> On 04/25/2013 07:18 AM, Gábor Lehel wrote:
>
>>
>> Whether to respond to failure by failing the task or by indicating it in
>> the return value seems like it would be better handled by having separate
>> functions for each. In the case where you expect success, the result
>> shouldn't be an Option. If the result is an Option, it shouldn't fail the
>> task (whether or not a condition handler is present). So for example:
>>
>> open(path: &P, mode: FileMode, access: FileAccess) ->
>> FileStream
>>
>> try_open(path: &P, mode: FileMode, access: FileAccess) ->
>> Option
>>
>> If the expect-success version of a function returns (), the try_ version
>> would return bool rather than Option<()> (which are isomorphic).
>>
>> Upon encountering an error, open() would raise a condition, and then
>> continue if possible, otherwise fail!().
>>
>> I'm not sure whether try_open() would raise a condition or not. The way
>> conditions were described, if they aren't handled, the task fails.
>> try_open() definitely shouldn't fail. Would it be reasonable to raise a
>> condition and instead of failing, return None if it's not handled? If not,
>> then try_open() shouldn't raise any conditions, and should just return None.
>>
>> The obvious drawback is twice as many functions, but it feels preferable
>> to forcing two different behaviours onto the same function. The fast
>> (expect-success) path would be just as clean as before, if not cleaner.
>>
>> [1] https://mail.mozilla.org/pipermail/rust-dev/2012-October/002545.html
>>
>
> With this strategy could we make the `try_` return values return
> `Result` (not using conditions at all)?


Don't see why not. The other option would be to return Option and provide a
(task-local) function to retrieve the details of the last error (I think I
saw something like that mentioned somewhere). I don't have a clear
preference.



> Then nil-returning functions would need to be `Option`.
> Having two ways to perform every I/O operation will 'infect' all code that
> uses I/O indirectly. For instance, we have a `ReaderUtil` trait that
> defines maybe two dozen methods. Do those all get duplicated? That seems
> pretty disastrous to me.
>

That's a good point. If all higher-up IO libraries end up having to write
two versions of everything too, that's pretty bad.

Ideally, you'd want to have everything do it one way consistently, and
provide some convenient method to translate it to the other way. But if the
default is condition-raising/fail!()ing, then the translation to Option
involves spawning a task which is unacceptably expensive, and if the
default is Option, then the translation to fail!() involves mucking with
Options, which makes straightline expect-success code unacceptably ugly. It
feels like a failure of the language if there's no adequate way to abstract
this out, honestly, I don't know what else to say. None of the options look
appealing. (If you have to abuse conditions and Options in counteridiomatic
ways to cram both behaviours into half the functions in a workable way,
that doesn't reflect well on the language either.)

If the recoverable/substitute-providing condition raising versions can be
done satisfactorally, would be that remove the need for the try_ versions?
I think you'd still want some way to say "okay, give up on opening the
file, let's do something else instead", without failing the task. Which is
presumably what NullFileStream would be about. But removing these kinds of
hidden error conditions is what Option is supposed to be about. Going in
circles...


> You mentioned disliking the giant IoError enum, and I agree, but I don't
> know a better way to represent it. We don't have subclassing to create an
> Exception type hierarchy. Do you have any suggestions?
>

I was only thinking that instead of

condition! { io_error: super::IoError -> (); }

you might have

condition! { file_not_found_error: super::FileNotFoundError -> Foo; }
condition! { file_permission_error: super::FilePermissionError -> Bar; }

and so forth. In other words, instead of the branching in the IoError enum,
branching at the condition and error-describing-type level. That only makes
sense in the as-you-would-expect-a-condition-system-to-work scenario though.

(FWIW, it might be possible to emulate an exception hierarchy
Haskell-style[1] like:

trait Exception: Typeable {
fn to_exception<'lt>(&'lt self) -> &'lt Exception;
fn from_exception<'lt>(&'lt Exception) -> Option<&'lt Self>;
}

tra

Re: [rust-dev] Update on I/O progress

2013-04-25 Thread Gábor Lehel
On Fri, Apr 26, 2013 at 2:29 AM, Brian Anderson wrote:

> It's almost taboo here, but we could consider adding catchable exceptions.
>

...which would let you default to the failing/throwing versions of
functions, and allow (or obviate) an efficient translation to
Result/Option. (Though: the None/Err cases would presumably still be slower
than a direct implementation, exception handling isn't typically considered
a fast path either. Is there a use case where that's a problem?)

I don't know how I feel about the prospect of catchable exceptions. Is
there a way to do them that doesn't suck? (i.e. other than how C++ or Java
do them)

The other direction would be some kind of syntax sugar to make interacting
with Options/Results seamless (less seamful), but I have no idea how that
might look.


 I was only thinking that instead of

 condition! { io_error: super::IoError -> (); }

 you might have

 condition! { file_not_found_error: super::FileNotFoundError -> Foo; }
condition! { file_permission_error: super::FilePermissionError -> Bar; }

 and so forth. In other words, instead of the branching in the IoError
enum, branching at the condition and error-describing-type level. That only
makes sense in the as-you-would-expect-a-condition-system-to-work scenario
though.



This is attractive, but how could you then create a block of code that
> trapped *all* errors?
>

Good question. That would only make sense if there's also a uniform way to
recover from all the errors. I think you could do it by making a
trap()/cond()-like that takes a fn(IoError) -> RecoveryType (where IoError
is an enum wrapping all of the specialized error types), installs condition
handlers for all of the specific conditions, and delegates all of them to
the general IoError-handling closure. So yeah, you'd still have a big
IoError enum, but you would only have to deal with it if you really did
want to handle all of the possibilities.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Update on I/O progress

2013-04-26 Thread Gábor Lehel
Probably a crazy idea, but would it be possible to do something where if
the parent task launches a child task, and then does nothing else except
wait for the child to finish, it's not actually implemented by launching a
new task, but instead by calling the closure in place? It seems
semantically equivalent, superficially. Presumably there would be
(insurmountable?) complications with task-local storage, the managed heap,
(and actually making task unwinding stop at the boundary), and so forth.
But (if it's not totally unreasonable) it would allow an efficient
implementation of try() and not be catchable exceptions.

On Fri, Apr 26, 2013 at 5:41 AM, Graydon Hoare  wrote:

> On 25/04/2013 5:29 PM, Brian Anderson wrote:
>
>  It's almost taboo here, but we could consider adding catchable exceptions.
>>
>
> For reference sake on this taboo -- let it not be too opaque! -- I will
> point to the amazing powers of IRC logging, and the conversation that
> contains the memorable phrase:
>
>please remind me of these things next time someone asks
> us about "resumable exceptions"; I had forgotten how
> convinced I was of this last time through the design-space
>
> http://irclog.gr/#show/irc.mozilla.org/rust/381023
>
> In particular, to summarize for the impatient: once you get resumable
> exceptions, your code can only be correct if it leaves every data structure
> that might persist through an unwind-and-catch (that it acquired through
> &mut or @mut or the like, and live in an outer frame) in an
> internally-consistent state, at every possible exception-point.
>
> I.e. you have to write in transactional/atomic-writes style in order to be
> correct. This is both performance-punitive and very hard to get right. Most
> C++ code simply isn't correct in this sense. Convince yourself via a quick
> read through the GotWs strcat linked to:
>
> http://www.gotw.ca/gotw/059.htm
> http://www.gotw.ca/gotw/008.htm
>
> Now granted, we don't have 'operator=' overloading, so some quantity of
> this is exaggerated. But not much. Any nontrivial sequence of writes and
> other-operations winds up requiring transactional treatment if you have
> resumable exceptions. This is a big tax.
>
> If we stay away from that feature, we get to hold on to sequential logic.
> Your can reason about state strictly in terms of "the sequence of
> operations as-written", or prefixes thereof. You never have to think about
> "someone might call another method on this object after it was
> half-modified and unwound mid-operation".
>
>
>  I was only thinking that instead of
>>>
>>> condition! { io_error: super::IoError -> (); }
>>>
>>> you might have
>>>
>>> condition! { file_not_found_error: super::FileNotFoundError -> Foo; }
>>> condition! { file_permission_error: super::FilePermissionError -> Bar; }
>>>
>>> and so forth. In other words, instead of the branching in the IoError
>>> enum, branching at the condition and error-describing-type level. That
>>> only makes sense in the as-you-would-expect-a-condition-system-to-work
>>> scenario though.
>>>
>>
>> This is attractive, but how could you then create a block of code that
>> trapped *all* errors?
>>
>
> I would be _totally_ amenable to enhancing the condition-declaring system
> to provide a means of chaining the default of a condition into raising a
> different one at the declaration-site. We have to do some further hacking
> on the condition-declaring system anyways, to handle passing & properly.
>
> At the moment, you _could_ do this if you were fastidious about it at the
> raise sites. Namely:
>
>   condition! { file_not_found_error: (Path) -> File; }
>   condition! { general_file_error: (Path, File) -> File; }
>   condition! { general_io_error: ~RtStream -> ~RtStream; }
>
>   ...
>
>   let p = Path::new(...);
>   ...
>
>   do file_no_found_error.cond.raise_default(copy p) {
>   let f = File::null_file();
>   do general_file_error.cond.raise_default(p, f) {
>   File::stream_file(general_io_error.cond.raise(f.rts));
>   }
>   }
>
> If you moved this kind of idiom into a helper function, you could call it
> from multiple locations without much boilerplate or possibility of getting
> the chaining wrong. But it'd be nicer to do it at condition-declaration
> site, certainly. More arguments for revisiting the condition! macro.
>
>
>  Unfortunately there is no equivalent to `Typeable` yet in Rust, but we
>> have talked about it (called `Any`). I would also like `fail!()` take an
>> `Any`, so this seems like an appropriate place for it.
>>
>
> +1 yes please. I suspect we're _close_ to having enough machinery for this
> now.
>
> -Graydon
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Update on I/O progress

2013-04-29 Thread Gábor Lehel
On Fri, Apr 26, 2013 at 11:11 PM, Brian Anderson wrote:

> On 04/26/2013 09:04 AM, Patrick Walton wrote:
>
>> On 4/26/13 6:53 AM, Gábor Lehel wrote:
>>
>>> Probably a crazy idea, but would it be possible to do something where if
>>> the parent task launches a child task, and then does nothing else except
>>> wait for the child to finish, it's not actually implemented by launching
>>> a new task, but instead by calling the closure in place?
>>>
>>
>> The new scheduler is designed to allow *almost* this -- `spawn` will
>> simply grab a cached stack segment (assuming there's one available) and
>> context switch to the task immediately.
>>
>
> Yeah, and I'm trying to make it so most of the resources held by tasks,
> even the task object itself, is cacheable. So when you spawn in an ideal
> scenario you occupy the dead husk of some other task and just start
> running. We can also probably lazily initialize *all* the local services
> (logging, local heap, gc, etc). We're going to have a lot of options to
> reduce the expense of spawning once things are written in Rust.


That sounds great.

But (I thought) one of our premises was that it's not viable to provide
only expect-success versions of IO functions, which raise conditions and/or
fail the task on encountering an error (instead of returning a
bool/Option/Result), because if you _do_ want to catch the error (e.g. in a
Result) and react some other way, you have to launch a new task to do it
(e.g. via try()), which is not fast enough for the use case.

But given what you wrote above, would it actually be fast enough? Or why
not?

Initially I wasn't interested, but now I also want to comment on close().
If I didn't miss something, the use cases for close() were:

1. Maybe you want to close the file early

Which thanks to affine types can be accomplished by simply `let _ = file;`
to make it go out of scope. (You could also write a function called close()
which consumes it, with an empty implementation.)

2. What if you want to see the error code?

Perhaps the consuming close() could return it?

3. What if it's in a managed box, in which case all of that doesn't
work?

In that case instead of @FileStream you should use @mut Option.
Then if you want to close the file, you assign None to it. That way you get
to retain the invariant that if you have a FileStream, it's open. If a file
is not open, either you don't have a FileStream, or "you" don't exist
because the task failed. That sounds pretty nice.

(FWIW the above was already mentioned in a previous message, but I missed
or forgot about it, so I hope there's no harm in repeating it in case
anyone else is similar.)

The fly in the ointment would seem to be that close() isn't the only way a
file can "go bad". After all, it can get into an erronous state after
almost any operation. But if in all of those cases, the operation responds
by failing the task unless a condition handler can repair the error, then
the invariant continues to hold. The type system enforces that tasks can't
share data, so if you want to try() an operation on a file, you have to
relinquish your ownership over the file, and if you want to get it back,
the only way is to put it in the Result of the try() -- which will be an
Err in the case of an error. Kind of like proto!. (Affine types are really,
really neat.)

Summa summarum it seems to me that if all IO functions either consume the
file (e.g. close()) or respond to unrecovered errors by fail!()ing, then
the "validity" of files can be tracked in the type system. I think that
would be really nice to have. Its viability is predicated on try() being
fast enough, which ties back to the previous discussion.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Simpler/more flexible condition handling was: Re: Update on I/O progress

2013-04-29 Thread Gábor Lehel
On Sat, Apr 27, 2013 at 8:50 PM, Patrick Walton  wrote:

> Perhaps we could introduce a form which is like "spawn a task to catch an
> exception" from a data sharing view (i.e. it takes an ~fn and can't close
> over any `@` data) but is optimized to just call the closure and trap
> failure instead of actually spawning a whole new task.
>

FWIW this sounds extremely similar to what I was proposing in the other
thread. I formulated it there as a general-case optimization that could
apply whenever you spawn a task and immediately wait for it to finish, but
a special case form would be just as good for the particular use cases
under discussion (mainly try(), are there others?). So if that's something
that could be done (I'm ignorant of the implementation details), it sounds
like great news.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] sub-grammar for range pattern constants?

2013-04-30 Thread Gábor Lehel
On Tue, Apr 30, 2013 at 6:57 PM, Tim Chevalier wrote:

> On Mon, Apr 29, 2013 at 9:30 PM, Erik S  wrote:
> > On 4/29/2013 2:00 PM, Graydon Hoare wrote:
> >> Yes. Formalizing and completing the definition of the constant
> >> expression subgrammar would be extremely valuable. It's one of the
> >> major high-risk areas remaining the the design space. -Graydon
> > VHDL is a very different language from Rust (it's a hardware design
> > language, not a normal programming language), but it has the nicest
> > constant system I've worked with.
> >
> > The rule is basically that constant expressions can contain:
> >
> > Literals (i.e. 5, 0x20)
> > Expressions that depend on constants  (i.e. 0x5 + 10 * const_k)
> > Any "pure" function whose arguments are constant.   (i.e. 5 + f_pure(5,
> > const_k) )
> >
> > It's this last rule that is truly beautiful. You can use the same
> > functions in both constant initialization and run-time code, with only
> > the requirement that they are pure. Pure functions are ones whose output
> > depends only on their arguments (and constants). Allowing functions to
> > initialize constants avoids the whole annoyance in C/C++ where you have
> > to use macros to make things compile-time const. It also allows a
> > surprising amount of compile-time optimization.
> >
> > I don't know how realistic this is for constants in a language like rust
> > - but it would be very elegant if it could work.
> >
>
> We used to have a notion of pure functions in Rust, but that was
> removed recently. It originally existed for typestate (a feature we
> removed a while ago) and more recently to assist the borrow checker.
> We removed it because in Rust, it was impractical to define an
> easily-checkable pure subset of the language. Bringing it back in is
> somewhat less likely than adding significant whitespace.
>
> Cheers,
> Tim
>

I thought an effects system (encompassing purity) is something that was
vaguely planned for 2.0?


>
>
>
> --
> Tim Chevalier * http://catamorphism.org/ * Often in error, never in doubt
> "Too much to carry, too much to let go
> Time goes fast, learning goes slow." -- Bruce Cockburn
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] PSA: ~"string" is probably not what you want

2013-04-30 Thread Gábor Lehel
On Sun, Apr 28, 2013 at 8:14 PM, Patrick Walton  wrote:

>
>
> > Is
> > there some way to make it just work, no matter what kind of strings
> > you're comparing?  Perhaps "foo" == (*x) would work, for example?
>
>  That doesn't work, because it makes the dynamically sized `str` a type,
> which is incoherent. (It would lead to dynamically sized stack frames, or
> structs or enums with infinite size, and so on.)
>
>
Couldn't this be relaxed? In other words allow dynamically sized `str` as a
type (and perhaps similarly for other dynamically sized types), but
prohibit those things specifically which would be problematic, i.e. using
it in ways that would require knowing its size? I think this would
essentially mean no variables, members, or parameters of that type, but you
*could* use it as a type argument -- including to the various pointer type
constructors. You might also have a rule whereby dereferencing a variable
when the result would be a dynamically-sized type is allowed *if* the
result is immediately borrowed. Then instead of `impl Eq for {@str, ~str,
&str}`, you would have just `impl Eq for str`, and if you want to compare
an ~str you dereference it.  Which seems logical to me, after all
ostensibly you want to compare the contents and not the pointer.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] PSA: ~"string" is probably not what you want

2013-04-30 Thread Gábor Lehel
On Tue, Apr 30, 2013 at 8:27 PM, Patrick Walton  wrote:

> On 4/30/13 11:26 AM, Gábor Lehel wrote:
>
>> Couldn't this be relaxed? In other words allow dynamically sized `str`
>> as a type (and perhaps similarly for other dynamically sized types), but
>> prohibit those things specifically which would be problematic, i.e.
>> using it in ways that would require knowing its size? I think this would
>> essentially mean no variables, members, or parameters of that type, but
>> you *could* use it as a type argument -- including to the various
>> pointer type constructors.
>>
>
>  Unfortunately that doesn't work, because opaque type variables `T` can
> still be moved, including into the local stack frame.
>

This did actually occur to me while writing the previous, along with early
thoughts about what you could do about it, but I thought I wouldn't get too
far ahead of myself. My thought process so far has been:

- OK, so maybe you would need a new built-in trait to track it, say
StaticSized, which you would need if you want to do that kind of thing with
a type variable.

- But that's the *common* case. You would have to write StaticSized
everywhere! Clearly that's not viable.

- OK, so maybe there's a way to invert it? ...but that doesn't really make
any sense, after half a minute of reflection.

- What about inference? The compiler checks how the type parameter is used,
and if it's used in way that requires knowing its size, the compiler puts
an implicit "must be statically sized" constraint on it, after which
instantiating it with a dynamically sized type produces an error at the
point of instantiation. (Not having it be obvious in the source at the
point of declaration whether a type variable is allowed to be dynamically
sized would be an annoyance, but I think a minor one. In the generated
documentation it could maybe be indicated by italics, or their absence.)

So that's where I am right now. What about inference? (I recall reading
that co/contravariance is also handled by inference, but I don't know if
that has any parallels with this beyond the superficial.)

Another thing this might allow is user-defined dynamically sized types.
Putting a dynamically sized type as the last member of a struct or enum
variant would be allowed (as per the common C idiom), in which case the
struct or enum itself would be considered dynamically sized, and have the
same rules apply.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] PSA: ~"string" is probably not what you want

2013-05-01 Thread Gábor Lehel
On Tue, Apr 30, 2013 at 9:22 PM, Graydon Hoare  wrote:

> On 30/04/2013 11:26 AM, Gábor Lehel wrote:
>
>  Couldn't this be relaxed? In other words allow dynamically sized `str`
>> as a type (and perhaps similarly for other dynamically sized types), but
>> prohibit those things specifically which would be problematic, i.e.
>> using it in ways that would require knowing its size?
>>
>
> This option was considered at ... great length, a year ago during the
> vector-reform conversations.
>
> https://mail.mozilla.org/pipermail/rust-dev/2012-April/001742.html
> https://mail.mozilla.org/pipermail/rust-dev/2012-April/001772.html
> https://mail.mozilla.org/pipermail/rust-dev/2012-June/001951.html
> https://mail.mozilla.org/pipermail/rust-dev/2012-July/002114.html
>
> I'm not sure anyone ever reduced those threads to their essence, but
> re-reading them I think I can articulate the fundamental difficulty with
> what you're suggesting:
>
>   - Any object has a real size. Some sizes are statically known,
> some must be discovered dynamically (by reading a size field
> or carrying a size value in a (ptr,size) pair)
>
>   - When T is static-size, &T and ~T and @T should be 1-word
> pointers. The compiler knows the size.
>
>   - To operate on a vector of statically-unknown size, you
> need to get its dynamically-known size from somewhere.
> This means pointers to vectors need to carry bounds.
>
>   - So ~[] and @[] and &[] are not the same representation as
> ~T, @T and &T in general. They have to have a size stuck
> on them somewhere.
>
>   - We want to be able to take sub-slices and have slices that
> point to fixed-size vectors in C structs. This means
> slices can't have their length in the pointee, and have to be
> (ptr,len) pairs.
>
> So about the only wiggle room away from where we are now is that we might
> be able to make ~[] represented by (ptr,len) pairs too, like slices are,
> rather than 1 ptr that points to a [len,data...] buffer. But it's not clear
> if that would buy us anything. Maybe a bit more genericity in impls, though
> I don't know how; Niko might. There might be a bit more room for
> improvement here, but it's an _extremely_ constrained space to work in.
>
> -Graydon
>
>
Thanks for the explanation! That makes a lot of sense.

I also just read Niko's blog post, and I'm not sure which thread to reply
in (or who to reply to), but I guess I'll do it here. Niko's message here
beforehand was kind of expectations-downplaying, but reading the blog post,
his proposed scheme seems to allow more or less the same as what I was
asking about here (perhaps minus the user-defined dynamically sized types,
but that was icing).

So *if* the plan ends up working out, then taking the second part of my
earlier proposal:

> You might also have a rule whereby dereferencing a variable when the
> result would be a dynamically-sized type is allowed *if* the result is
> immediately borrowed. Then instead of `impl Eq for {@str, ~str, &str}`,
> you would have just `impl Eq for str`, and if you want to compare an
> ~str you dereference it.

Would that work? Would it be a good solution?

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] PSA: ~"string" is probably not what you want

2013-05-01 Thread Gábor Lehel
On Wed, May 1, 2013 at 4:50 PM, Niko Matsakis  wrote:

> On Wed, May 01, 2013 at 02:39:12PM +0200, Gábor Lehel wrote:
>
> > So *if* the plan ends up working out, then taking the second part of my
> > earlier proposal:
> >
> > > You might also have a rule whereby dereferencing a variable when the
> > > result would be a dynamically-sized type is allowed *if* the result is
> > > immediately borrowed. Then instead of `impl Eq for {@str, ~str, &str}`,
> > > you would have just `impl Eq for str`, and if you want to compare an
> > > ~str you dereference it.
> >
> > Would that work? Would it be a good solution?
>
> I believe that would be the plan, yes. Most of these
> "apply-to-almost-everything"
> traits, like `Eq` or `Ord`, could be implemented in a similar fashion.
>
> I'm not sure what problem you are proposing this as a solution *for*,
> though.  Do you mean the problem of comparing strings using `==`?
>

Yeah. Which was the original topic of this thread... :)


>
> I suppose it is true that under this proposal you could write
>
> let str1: ~str = ~"hi";
> let str2: &str = "hi";
> *str1 == *str2
>
> and it would work fine. That is a nice side-effect I hadn't
> considered.
>

Right. That was the intent. And similarly(?) for comparing against string
literals. (Though I'm not completely clear about when auto-borrowing does
or doesn't happen, and how that would interact with this.)


>
> *An aside:* Note that dereferencing a pointer to an unsized object is
> only allowed in an "lvalue" context (that is, when we are taking its
> address):
>
> let str1: ~str = ~"hi";
> let foo = *str1; // ERROR
> let bar = &*str1; // OK
>
> Here, `foo` is an error because `*str1` is being evaluated in an
> rvalue context, but `bar` is fine, because `*str1` is being evaluted
> in an lvalue context. In the case of `==`, this works out because the
> arguments to overloaded operators are always passed by reference.
>

Yep, that's what I was figuring. I'm not sure what "lvalue context" means
precisely -- it's not actually on the left hand side of anything here, and
the other example below where it *is* on the LHS is illegal -- but the
shape of things matches what I was expecting.


>
> *A further aside:* don't be misled by my use of the term
> "lvalue context" into thinking that a program like this would
> be legal:
>
> let mut str1: ~str = ~"Hello";
> *str1 = "World"; // ERROR
>
> This is illegal because assigning to an lvalue of unsized type is
> illegal, even though `*str1` appears in an lvalue context.
>

Hmm. If I'm thinking right this is because the size of the string is stored
in the pointer, which, if the string gets changed behind its back, would
become invalid?


>
>
>
> regards,
> Niko
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Pattern matching binding operator

2013-05-03 Thread Gábor Lehel
On Fri, May 3, 2013 at 3:12 AM, Patrick Walton  wrote:

>
> The alternative is `as`, like OCaml. However, this conflicts with `as` in
> the expression grammar. A subset of the expression grammar is part of the
> pattern grammar in order to permit matching against constants. Removing
> `as` expressions from the subset of expression productions permitted in
> patterns would mean that this would no longer do what you expect:
>
> match 22.0f32 / 7.0f32 {
> math::PI as f32 => println("Good grief!"),
> _ => {}
> }
>


You could emit a warning or an error if the bound name is the same as the
name of a type.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] sub-grammar for range pattern constants?

2013-05-03 Thread Gábor Lehel
So this has been bouncing around in the back of my head, and now actual
thoughts have congealed there. Essentially, while I think this is a very
good defense of why Rust doesn't have purity, it's not so convincing to me
as a defense of why it *shouldn't* have purity. (I don't know if it was
intended as one.)

On Tue, Apr 30, 2013 at 8:38 PM, Graydon Hoare  wrote:

> On 30/04/2013 10:08 AM, Max Cantor wrote:
>
>  I know this will be an unpopular opinion, but pure functions would be a
>> massive win for Rust, especially if the eventual goal is high
>> performance, highly parallelizable (browser rendering engines..)
>> development.
>>
>
> Careful. It's important to understand that "purity" seems like it has a
> simple definition but in languages with mutable memory, state, time and IO,
> it gets hard to be exact.
>
> Things you can throw at a SIMD unit or GPU and get parallel kernels out of
> them will almost certainly be a different version of "pure" than things you
> can evaluate at compile time. Or, as in our previous attempts at defining
> it, things that won't break existing borrows or existing typestates. Each
> of these is a static approximation of the
> set-of-all-things-a-function-might-do. Since our functions can generally do
> quite a lot, the set of possible subsets you might mean by "pure" is
> correspondingly much larger.
>
>
>  The typestate system did seme very complex but isn't there a middle
>> ground via annotations perhaps?  A subset of primitives and core
>> functions can be annotated as pure and then any function calling only
>> pure functions can itself be annotated as pure.
>>
>
> This gets difficult fast. You wind up dividing your functions up into
> groups and then getting annoyed that one that's "mostly almost pure" or
> "essentially pure, for my purposes" that you wanted to call actually isn't
> (someone forgot to mark it as such, or some sub-function, or some
> trait-implied function) and then you can't. Or is pure using one way of
> thinking about purity, but not another. Or is pure except for the bit where
> it calls unsafe but promises it's going to maintain purity, just knows
> better than you (oops, that can't be done at compile time, nor on a GPU,
> etc.)
>

Yeah, this is definitely a valid observation. Even Haskell is bitten by
this (if something always has the same value in a single run of the program
but may have different values in different runs, is it pure?).


>
> C++ has multiple concepts for this, each a not-entirely-obvious subset of
> the others, each affecting the others, and causing quite a lot of work just
> to get things to compile, much less reuse code.
>

C++ is C++. It has multiple concepts for *everything*. That something is
complicated in C++ does not imply that it has to be complicated by
necessity.


>
> They have const methods (don't mutate the object, unless you lie and
> override it) and constexpr (can be evaluated at compile time), and macros
> (can only operate on tokens), and template evaluation (can only operate on
> certain non-type args), and the openCL __kernel extension for
> GPU-applicable functions:
>

This is unfair even to C++. Const methods are the same thing in spirit as
`&self` versus `&mut self` in Rust. Macros in C++ and Rust alike are for
code generation, not computation. I wasn't familiar with __kernel, but I
looked into it[1] and found this: "The function using the __kernel
qualifier can only have return type void in the source code". If that has a
common intersection with any other definition of purity, it's `return ();`.

That leaves templates and constexpr. Which, indeed, have a lot of overlap,
and neither is a subset of the other (constexpr functions can't do types
while templates can't do values of user-defined types other than enums, for
example). Both are restricted to dealing in things that can be evaluated at
compile time.* The part of templates that deals in types corresponds to
Rust's generics, while the part of templates that can calculate the
Fibonacci numbers is a historical semi-accident. Constexpr expressions (and
hence functions) correspond to Rust's attempts to define a constant
expression subgrammar.

The only really legitimate notion of purity in C++ to my mind is constexpr.

[1]
http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/restrictions.html

* Actually, constexpr functions *may* contain non-constexpr expressions,
but only as long as they are in the branch of a conditional which isn't
taken at compile time. Which is actually useful. C++ for you.


>  "Which purity do you mean" is a very real question, not one you can just
> brush aside. The combinations are worse, in that they tend to cause (or
> require) subtyping relationships, that touch _everything_ in the language,
> from inference and mandatory syntax (which types get inferred when you just
> write a lambda?) to type checking (which calls are legal, which impls and
> delegations are legal) to codegen (which LLVM attributes are legal? which
> 

Re: [rust-dev] Adding exception handling as syntax sugar with declared exceptions

2013-05-18 Thread Gábor Lehel
On Thu, May 16, 2013 at 7:58 PM, Graydon Hoare  wrote:

>
> I'm sympathetic to the desire here, as with all attempts to "get
> exceptions right". Sadly I've never really seen it; I don't think anyone
> has really worked out the "right" way to work with catchable-exceptions in
> a language.
>
> -Graydon
>

What's problematic about exceptions in C++, and what forces you to "worry
about exceptions everywhere", is that code inside a try-block and code
outside of it may share state, which the code in the try block may be in
various stages of modifying (along with allocating resources) when the
exception is thrown, potentially leaving an inconsistent state and/or
leaked resources at the point where the exception is caught. Therefore all
functions have to be very careful that any mutable state accessible to the
function is in (or reverts to) a consistent state at any point where an
exception might be thrown, and that resources aren't left dangling, because
who knows whether that mutable state might not be on the outside of a
try-block and the function on the inside of it.

What if instead of that, the language enforced that code in try blocks
could not share any state with code outside, only allowing for data to be
transferred in at the beginning and out (whether a result or an exception)
at the end, and ensured that all resources acquired inside the try block
were destroyed after it exited, no matter how it did? That would free the
authors of individual functions from having to care about any of it,
because if an exception is passing through their code, that means that from
their perspective "the world is ending", everything they have access to
will be destroyed, so they can do whatever they want and it won't matter a
bit.

If that sounds familiar at all, it's because I just described the semantics
of Rust's existing try()*. I still suspect that the best of all worlds
would be something with the semantics of try(), or close, and the
implementation and performance of traditional EH. Is that unrealistic?



* Along with the ability to actually observe the thrown exception, but IINM
that's planned. And I guess you could have multiple variations on how to
handle catching-and-or-rethrowing. If the plan is to use dynamic typing for
the exception object you could have something like:

impl Result {
fn catch(self, hndlr: &fn(ex: ~E) -> T) -> T {
match self {
Ok(res) => res,
Err(ex) => {
match ex.cast::() {
Ok(casted_ex) => hndlr(casted_ex),
Err(uncasted_ex) => fail!(uncasted_ex)
}
}
}
}
}

That doesn't really do "catch block" chaining well and I probably made 10
type errors, but oh well.


--
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


[rust-dev] Modest proposal: Make `pub` the default everywhere, possibly remove it

2013-05-29 Thread Gábor Lehel
Hello list,

Currently Rust has different default visibilities in different places:
- `mod` items are private
- `struct` fields and `enum` variants are public
- `trait` methods are public
- `impl` methods are private
- `impls for` are public (by necessity)

I propose to change this to:
- Everything not `priv` is public

Points in favor:

- It's much easier to explain, grok, and remember.

- Most definitions are written to be public. Writing `public` in front of
every dang thing was annoying in Java and C#, and it's no less annoying in
Rust. Maybe especially so for those coming from C/C++, who haven't already
resigned themselves to it.

 - Currently `impls for` are always public, while other items including
`impl`s are private. This would make it consistent and remove a potential
source of confusion. And instead of a deep-seated semantic rule for users
to remember, there would be a simple syntactic one: you can't put `priv` on
an `impl for`.

- I get the reason for the `priv` default and requiring `pub` explicitly,
and initially agreed with it: that module authors should think about what
they export, therefore the burden should be on those cases where you want
to make something public. But in my experience, module authors are very
conscientious about their public interfaces, and put special importance on
what should be kept hidden. Having this be explicit with `priv` isn't
necessarily a disadvantage.

- Reading a module for its interface won't be any worse: instead of looking
for `pub`s and skipping things without it unless if they're `impls for`,
you would read everything and skip the `priv`s.

- The `pub` keyword could potentially be removed from the language.

Issues:

- Public by default clearly can't extend to `use`, which should remain
private. That means `pub use` is left as an orphan. Either it could remain
`pub`'s only use, or it could be expressed some other way.

Thanks for considering,
Gábor

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Modest proposal: Make `pub` the default everywhere, possibly remove it

2013-05-29 Thread Gábor Lehel
Maybe I'm unusual. But in C++ I'm always super-careful (one might say anal
retentive) about keeping my headers as clean of implementation details as
humanly possible. I don't think forgetting a `priv` would ever be an issue.
But I'm not everyone.

On Wed, May 29, 2013 at 11:34 PM, Mikhail Zabaluev <
mikhail.zabal...@gmail.com> wrote:

> Hi,
>
> I have a strong point against it: your crates are going to be christmas
> trees of exposed APIs unless you are careful. This has a runtime cost,
> increases the risk of breaking the ABI more often than you should, and
> often results in unintentional APIs that you may be later beholden to
> maintain.
>
> Look at the tragedy of failing to use -fvisibility=hidden when building
> shared libraries with gcc.
>
> Best regards,
>   Mikhail
>
>
> 2013/5/30 Gábor Lehel 
>
> Currently Rust has different default visibilities in different places:
>> - `mod` items are private
>> - `struct` fields and `enum` variants are public
>> - `trait` methods are public
>> - `impl` methods are private
>> - `impls for` are public (by necessity)
>>
>> I propose to change this to:
>> - Everything not `priv` is public
>>
>


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Modest proposal: Make `pub` the default everywhere, possibly remove it

2013-05-29 Thread Gábor Lehel
On Wed, May 29, 2013 at 11:53 PM, Graydon Hoare  wrote:

> On 13-05-29 02:40 PM, Gábor Lehel wrote:
> > Maybe I'm unusual. But in C++ I'm always super-careful (one might say
> > anal retentive) about keeping my headers as clean of implementation
> > details as humanly possible. I don't think forgetting a `priv` would
> > ever be an issue. But I'm not everyone.
>
> Because Rust does not differentiate between "header" and
> "implementation" -- the signatures and metadata are extracted from the
> program and compiled-in to the resulting binary -- our experience when
> "everything was public" (as it once was, briefly) was that everything
> got exported. People just didn't notice all the things they were
> exporting, or deferred having to think about it.
>
> At a different time, we had explicit export lists, and a sort of "phase
> transition" when developing a library: everything was exported when you
> mentioned nothing and were just doing sketches; but as soon as you
> mentioned exports, _only_ those things were exported.
>
> This facility was removed when we switched to pub and priv, on the basis
> that people complained about having to scroll up and down the screen
> looking for the export list.
>
> I think there is no perfect answer here. I'm sympathetic to your
> concerns ('pub' was chosen as small as possible, to minimize these
> costs) but I think that making everything public by default is a major
> hazard for growing private-API dependencies, and would not want us to go
> there.
>
> -Graydon
>
>
My proposal does rest on the assumption that module authors know what
they're doing and wouldn't leave things exported accidentally (won't the
exported items showing up in the generated docs dissuade them, at least?),
but if that's not true then I agree the proposal is much less attractive.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Adding exception handling as syntax sugar with declared exceptions

2013-06-06 Thread Gábor Lehel
On Sat, May 18, 2013 at 2:24 PM, Gábor Lehel  wrote:

>
>
> On Thu, May 16, 2013 at 7:58 PM, Graydon Hoare wrote:
>
>>
>> I'm sympathetic to the desire here, as with all attempts to "get
>> exceptions right". Sadly I've never really seen it; I don't think anyone
>> has really worked out the "right" way to work with catchable-exceptions in
>> a language.
>>
>> -Graydon
>>
>
> What's problematic about exceptions in C++, and what forces you to "worry
> about exceptions everywhere", is that code inside a try-block and code
> outside of it may share state, which the code in the try block may be in
> various stages of modifying (along with allocating resources) when the
> exception is thrown, potentially leaving an inconsistent state and/or
> leaked resources at the point where the exception is caught. Therefore all
> functions have to be very careful that any mutable state accessible to the
> function is in (or reverts to) a consistent state at any point where an
> exception might be thrown, and that resources aren't left dangling, because
> who knows whether that mutable state might not be on the outside of a
> try-block and the function on the inside of it.
>
> What if instead of that, the language enforced that code in try blocks
> could not share any state with code outside, only allowing for data to be
> transferred in at the beginning and out (whether a result or an exception)
> at the end, and ensured that all resources acquired inside the try block
> were destroyed after it exited, no matter how it did? That would free the
> authors of individual functions from having to care about any of it,
> because if an exception is passing through their code, that means that from
> their perspective "the world is ending", everything they have access to
> will be destroyed, so they can do whatever they want and it won't matter a
> bit.
>
> If that sounds familiar at all, it's because I just described the
> semantics of Rust's existing try()*. I still suspect that the best of all
> worlds would be something with the semantics of try(), or close, and the
> implementation and performance of traditional EH. Is that unrealistic?
>
>
Am I way off base with this? An embarrassing misconception? To summarize my
train of thought

 * Catchable exceptions can be implemented
 * But we don't want to, because it would force everyone to think about
exception safety
 * That could however be avoided with appropriate restrictions
 * Rust's type system already gives us the tools to impose those
restrictions, as evidenced by them being imposed on `try()`
 * Therefore it should be possible to have much of the benefit of catchable
exceptions, without their drawbacks

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Adding exception handling as syntax sugar with declared exceptions

2013-06-07 Thread Gábor Lehel
On Thu, Jun 6, 2013 at 7:00 PM, Graydon Hoare  wrote:

> On 06/06/2013 8:59 AM, Gábor Lehel wrote:
>
>  Am I way off base with this? An embarrassing misconception? To summarize
>> my train of thought
>>
>>   * Catchable exceptions can be implemented
>>   * But we don't want to, because it would force everyone to think about
>> exception safety
>>   * That could however be avoided with appropriate restrictions
>>   * Rust's type system already gives us the tools to impose those
>> restrictions, as evidenced by them being imposed on `try()`
>>   * Therefore it should be possible to have much of the benefit of
>> catchable exceptions, without their drawbacks
>>
>
> No. The train of thought is that they _already are_ implemented to this
> level -- via task isolation[1] -- and people asking for "catchable
> exceptions" are (so far) actually asking for us to lift those
> restrictions[2], which we don't want to do.
>
> -Graydon
>
> [1] If it helps avoid wincing about the implied "cost of spawning a task"
> (allocating a segment, switching to it, and switching back on return) it
> might help to know that there are some serious discussions going on in the
> background about cactus stacks and the requisite scheduler hooks required
> to support cilk-like fork/join parallelism.
>

Oh, okay. I wasn't aware of this sorry. What I knew was that a couple of
times, when I raised the possibility of relying on `try()` for error
handling, the response was that it wasn't fast enough and possibly never
would be (and in one instance the idea of catchable exceptions was floated
in response, suggesting that those *would* be fast enough), which is why I
thought maybe it would be better (despite costlier) to give in and have a
dedicated language feature, instead of only a library solution that's too
slow for actual use. But obviously satisfying all the relevant use cases
using only existing features is the best possible scenario!


>
> [2] And/or asking to add "first class" language support for the idioms in
> the form of new keywords, RTTI or additional control structures. In case
> this is not obvious: we are trying to move as much as possible these days
> _out_ of the core language and into libraries and macros. This usually
> results in faster iteration, less fragility, fewer personnel and
> development/integration bottlenecks, and overall better implementations.
>
>


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] 'in' for for loops and alloc exprs

2013-07-26 Thread Gábor Lehel
+1 for `for x in xs { }`
+1 for `in` as placement new, as far as I understand it
+1 for keeping `do` and `do 666.times { }`
+1 for `:` as type ascription (yay!)

On Thu, Jul 25, 2013 at 11:28 PM, Thad Guidry  wrote:

> I also like 'do 10,times {' , since I likely would also use it to create
> multiple copies (one after the other).  A lazy sequence for my purposes
> dealing with data generation.
>
> Just to clarify, 'do 10.times {' would NOT allow launching multiple copies
> in parallel, correct ?  (that's reserved for Actor styles, etc, etc.)  Just
> simple repetition, as you say ?
>
>
> On Thu, Jul 25, 2013 at 3:46 PM, Benjamin Striegel  > wrote:
>
>> > If we did that, we'd lose the ability to break or return, since 'do'
>> doesn't follow the for-style loop protocol.
>>
>> I'm perfectly happy with that; the `times` method was never about complex
>> control flow, just simple repetition. `do 10.times {` is a delightfully
>> self-explanatory contract.
>>
>> And just as evidence that `do` notation is useful beyond `spawn`:
>>
>> $ git grep "do .*{" -- "*.rs" | wc -l
>> 2019
>> $ git grep "do .*spawn" -- "*.rs" | wc -l
>> 274
>>
>> > we're likely to switch that to a macro call that passes
>> explicitly-captured arguments into a thunk struct that owns the explicit
>> captures
>>
>> I don't remember seeing much of this idea in the meeting notes, but I do
>> remember seeing it mentioned in these blog posts (for those who want more
>> context):
>>
>> http://smallcultfollowing.com/babysteps/blog/2013/05/30/removing-procs/
>> http://smallcultfollowing.com/babysteps/blog/2013/06/03/more-on-fns/
>>
>> ___
>> Rust-dev mailing list
>> Rust-dev@mozilla.org
>> https://mail.mozilla.org/listinfo/rust-dev
>>
>>
>
>
> --
> -Thad
> Thad on Freebase.com 
> Thad on LinkedIn 
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
>


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


[rust-dev] RFC: Removing *T

2013-07-27 Thread Gábor Lehel
Spurred by https://github.com/mozilla/rust/issues/7694 I was thinking about
the differences between `*` and `&` and the reason both need to exist.

As far as I can tell the differences boil down to:

  - The compiler makes no attempt to enforce any invariants for `*T`

Meaning that, in an interesting duality-ish:

 - Creating `*T`  out of `&T`  (or anything else) and manipulating it in
any which way is always safe
 - Dereferencing `*T` is unsafe
 - Creating `&T` out of `*T` is unsafe
 - Dereferencing `&T` (and whatever else the language lets you do with it)
is safe

Behind it is proof obligations. `*T` has no implied invariants and
therefore doesn't require proof of anything, while as long as you stick to
`&T`, safety is proved by the compiler. It's at the boundary where the
burden is on the programmer: to assert (with an `unsafe` block) that the
invariants required for dereferencing `*T` and/or converting it to `&T`
really do hold.

The use case for `*T` is operations which are not known to respect
invariants: notably foreign functions, also e.g. pointer arithmetic.

The invariants required of `&T` but not `*T` are:

  1. The pointer is not null
  2. The pointed-to object is of type `T`
  3. The pointed-to object is alive and initialized
  4. (Im)mutability and aliasing related invariants

The latter three of which are guaranteed for the lifetime associated with
the pointer.

Now crazy ideas:

We can waive the first invariant by using `Option`. If we could guarantee
in the language that `None : Option<&T>` is represented as a null pointer,
then I see no reason whatsoever to keep allowing implicit nullability. It's
binary compatible with C, so (except where can't-be-null is known) C
interfaces would simply use Option. It forces proper attention to nulls,
and I don't actually see any drawback.

We can waive the other invariants by taking advantage of the fact that
they're predicated on a lifetime, to introduce a new special lifetime
`'unsafe`, which is the inverse of `'static`. Where `'static` is always
known to be alive, `'unsafe` never is. (So `'static` is top and `'unsafe`
is bottom.) Therefore converting `&'a T` to `&'unsafe T` is always allowed,
while if you have an `&'unsafe T` and want to convert it to a pointer with
a longer lifetime and dereference it, you have to use `unsafe { }`,  just
as with `*T`. Functions parameterized over lifetime variables would
continue to require that the lifetime encompass the duration of the call,
so if you want to allow `&'unsafe T` as an argument, you have to write that
explicitly (again as with `*T`).

One question is whether you might want to waive 2-4. with finer
granularity:

- You could waive only 2. by using `&()`. It's not clear to me if it makes
sense to talk about a definitely-live-and-initialized(-and-immutable) value
of unknown type, but if there's a use case for it, it can be satisfied.

- I don't think it makes sense to waive only 3.: you can't say a dead or
uninitialized value is of type T, because it could be *anything* (which is
why `'unsafe` above, conceptually most closely tied to 3., also waives 2.
and 4.).

- It might make sense to waive only 4.: you might care only that a value is
alive and of type T, not whether anyone else is mutating it: this is
`&const T` (which is hoped to be removed). You might also want to mutate it
while not caring whether anyone else is also reading or writing: there's no
separate mutability qualifier for this, a use case might be e.g. atomic
operations. But even in the absence of any special features, these can
still be satisfied by using `&'unsafe [mut] T`, which is an
overapproximation, just as with `*[mut] T` today.

(Corresponding to the separate invariants, it might make sense to have
separate functions for unsafely manipulating the pointed-to type, lifetime,
and mutability of a borrowed pointer, instead of just the catch-all
`transmute()`, similarly to C++.)

Summary:

I think we could replace `*` pointers with a combination of guaranteeing
the null pointer optimization for `Option` (kinda like TCO) and introducing
a new special lifetime `'unsafe`. This would retain the desirable
properties of the current system, while being superior in other ways, such
as orthogonality. Whether and how to waive aliasing and mutability related
invariants separately from the others might need more thinking, but is
independent.

Thoughts?

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Removing *T

2013-07-27 Thread Gábor Lehel
Some discussion has sprouted on reddit:
http://www.reddit.com/r/rust/comments/1j5vbn/rustdev_rfc_removing_t/

On Sat, Jul 27, 2013 at 4:51 PM, Gábor Lehel  wrote:

> Spurred by https://github.com/mozilla/rust/issues/7694 I was thinking
> about the differences between `*` and `&` and the reason both need to
> exist.
>
> As far as I can tell the differences boil down to:
>
>   - The compiler makes no attempt to enforce any invariants for `*T`
>
> Meaning that, in an interesting duality-ish:
>
>  - Creating `*T`  out of `&T`  (or anything else) and manipulating it in
> any which way is always safe
>  - Dereferencing `*T` is unsafe
>  - Creating `&T` out of `*T` is unsafe
>  - Dereferencing `&T` (and whatever else the language lets you do with it)
> is safe
>
> Behind it is proof obligations. `*T` has no implied invariants and
> therefore doesn't require proof of anything, while as long as you stick to
> `&T`, safety is proved by the compiler. It's at the boundary where the
> burden is on the programmer: to assert (with an `unsafe` block) that the
> invariants required for dereferencing `*T` and/or converting it to `&T`
> really do hold.
>
> The use case for `*T` is operations which are not known to respect
> invariants: notably foreign functions, also e.g. pointer arithmetic.
>
> The invariants required of `&T` but not `*T` are:
>
>   1. The pointer is not null
>   2. The pointed-to object is of type `T`
>   3. The pointed-to object is alive and initialized
>   4. (Im)mutability and aliasing related invariants
>
> The latter three of which are guaranteed for the lifetime associated with
> the pointer.
>
> Now crazy ideas:
>
> We can waive the first invariant by using `Option`. If we could guarantee
> in the language that `None : Option<&T>` is represented as a null pointer,
> then I see no reason whatsoever to keep allowing implicit nullability. It's
> binary compatible with C, so (except where can't-be-null is known) C
> interfaces would simply use Option. It forces proper attention to nulls,
> and I don't actually see any drawback.
>
> We can waive the other invariants by taking advantage of the fact that
> they're predicated on a lifetime, to introduce a new special lifetime
> `'unsafe`, which is the inverse of `'static`. Where `'static` is always
> known to be alive, `'unsafe` never is. (So `'static` is top and `'unsafe`
> is bottom.) Therefore converting `&'a T` to `&'unsafe T` is always allowed,
> while if you have an `&'unsafe T` and want to convert it to a pointer with
> a longer lifetime and dereference it, you have to use `unsafe { }`,  just
> as with `*T`. Functions parameterized over lifetime variables would
> continue to require that the lifetime encompass the duration of the call,
> so if you want to allow `&'unsafe T` as an argument, you have to write that
> explicitly (again as with `*T`).
>
> One question is whether you might want to waive 2-4. with finer
> granularity:
>
> - You could waive only 2. by using `&()`. It's not clear to me if it makes
> sense to talk about a definitely-live-and-initialized(-and-immutable) value
> of unknown type, but if there's a use case for it, it can be satisfied.
>
> - I don't think it makes sense to waive only 3.: you can't say a dead or
> uninitialized value is of type T, because it could be *anything* (which is
> why `'unsafe` above, conceptually most closely tied to 3., also waives 2.
> and 4.).
>
> - It might make sense to waive only 4.: you might care only that a value
> is alive and of type T, not whether anyone else is mutating it: this is
> `&const T` (which is hoped to be removed). You might also want to mutate it
> while not caring whether anyone else is also reading or writing: there's no
> separate mutability qualifier for this, a use case might be e.g. atomic
> operations. But even in the absence of any special features, these can
> still be satisfied by using `&'unsafe [mut] T`, which is an
> overapproximation, just as with `*[mut] T` today.
>
> (Corresponding to the separate invariants, it might make sense to have
> separate functions for unsafely manipulating the pointed-to type, lifetime,
> and mutability of a borrowed pointer, instead of just the catch-all
> `transmute()`, similarly to C++.)
>
> Summary:
>
> I think we could replace `*` pointers with a combination of guaranteeing
> the null pointer optimization for `Option` (kinda like TCO) and introducing
> a new special lifetime `'unsafe`. This would retain the desirable
> properties of the current system, while being superior in other ways, such
> as orthogonality. Whether and how to waive aliasing and mutability related
> invariants separately from the others might need more thinking, but is
> independent.
>
> Thoughts?
>
> --
> Your ship was destroyed in a monadic eruption.
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Removing *T

2013-07-28 Thread Gábor Lehel
I've found the short circuit in my brain: the problem is that an (in
hindsight embarrassingly obvious) invariant of `&'a T` is that it's
outlived by whatever it's pointing at, which is what has the lifetime (at
least) 'a. So if 'unsafe is the empty lifetime, then `&'unsafe T` is an
oxymoron: it can't exist. What I was consistently confusing it with (I
think I might've relied on different interpretations in different places)
is the idea of a pointer which lives however long, but can only *be
dereferenced* within the given lifetime. I wonder if the latter is
theoretically feasible at all?

In any case, I think this also shows that the ban on `&'a &'b T` where 'a
outlives 'b is principled, and follows from simple transitivity: if the
given `&'b T` has lifetime at least 'a, and is outlived by the given T with
lifetime at least 'b, it follows that 'b must be greater than 'a.
Pseudo-formally:

1. 'a >= lifetime(given &'a &'b T)
2. lifetime(given &'b T) >= 'a
3. 'b >= lifetime(given &'b T)
4. lifetime(given T) >= 'b
-
'b >= 'a (from 2. and 3.)

On Sat, Jul 27, 2013 at 4:51 PM, Gábor Lehel  wrote:
>
> Summary:
>
> I think we could replace `*` pointers with a combination of guaranteeing
> the null pointer optimization for `Option` (kinda like TCO) and introducing
> a new special lifetime `'unsafe`. This would retain the desirable
> properties of the current system, while being superior in other ways, such
> as orthogonality. Whether and how to waive aliasing and mutability related
> invariants separately from the others might need more thinking, but is
> independent.
>
> Thoughts?
>
> --
> Your ship was destroyed in a monadic eruption.
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Removing *T

2013-07-28 Thread Gábor Lehel
On Sun, Jul 28, 2013 at 7:42 PM, Daniel Micay  wrote:
>
> On Sun, Jul 28, 2013 at 12:50 PM, Patrick Walton  wrote:
> >
> > I'm as sympathetic as anybody to want to reduce the number of pointer types
> > in the language, as it's folks' #1 complaint about Rust. But I think this
> > one is probably a necessary evil.
> >
> >
> > Patrick
>
> We can reduce the number of pointer types in the language by
> describing the language with semantic terms rather than implementation
> details of the compiler. The safe subset of Rust lacks pointers in the
> same way that a language like Ruby lacks them.
>
> For example, `~[T]` is described as a unique vector, despite being a
> pointer. It feels like we're going out of our way to make the language
> complex when we use a term like "borrowed pointer" instead of just
> calling it a reference like Java.
>
> The documentation would be so much simpler if it just referred to
> references and unique/managed boxes. Rust only has two types that are
> semantically pointers, `*` and `*mut`.

+infinity

--
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Removing *T

2013-07-28 Thread Gábor Lehel
On Sun, Jul 28, 2013 at 3:34 AM, Erick Tryzelaar
 wrote:
> Hey Gábor!
>
> This is a neat observation!

Thanks.

> I especially like how Option<&T> represents
> nullable pointers. However, I am not sure if a bottom region as you
> described quite captures the right semantics. While it's neat how it falls
> out naturally that you need to use an unsafe block to dereference a
> `&'unsafe T`, this bottom region also have a shorter lifetime then every
> other region. So this would mean we couldn't safely return these pointers
> without a cast.
>
> Because of this, I think pointers have a couple orthogonal semantics:
>
> 1. Mutablility
> 2. Safety
> 3. Lifetime
>
> If my unsafe region pointers patch lands, we support all these cases.

I think I still don't grok the meaning of lifetimes on *T. Is the
lifetime a necessary or sufficient condition for anything, or is it
just a helpful guardrail?

>
> That said, I still think there is a good argument about whether or not we
> need unsafe pointers in the library or in the language. bstrie brought up
> this idea of moving *T out of the language and into a library, as in an
> Unsafe or RawPtr type. If we implement RawPtr as:
>
> struct RawPtr;
>
> impl RawPtr {
>unsafe fn get(&self) -> T { intrinsics::get(self) }
>unsafe fn set(&self, x: T) -> T { intrinsics::set(self, x) }
> ...
> }
>
> Then I *think* it supports all the semantics we need. If I'm not missing
> anything, I'd rather have this, reject my patch, and remove unsafe pointers
> altogether from the compiler.

I think there's two independent questions.

One is syntax. *T or RawPtr? (@T or Gc?) Syntax sugar isn't
inherently good or bad: it's sweet, but too much can make you feel
bad. We have Lisp at one extreme and Perl at the other. We should
strive to find a pleasant balance. It might be worthwhile to have it
really be "just sugar" by making @T, ~T, &T, and *T be aliases for
distinguished types Gc, Owned, Ref<'a, T>, and Ptr (for
example), regardless of which syntax candies we ultimately keep (I
think I saw something like this in the bug tracker?). (Personally I
lean towards keeping, in part because nested This> is
unpleasant, and it's nice to avoid it .)

The other is implementation. In the compiler or as a library? This one
seems less ambiguous: anything that can be done in a library probably
should be. If we can put Gc, Ptr etc. in libraries and have the
compiler just do desugaring (if anything), why not? (I think this
wouldn't be possible for Owned, because it's not parametric in T
(the representation depends on whether T is managed)? Rust doesn't
have anything like C++'s template specialization. Won't all of them
have this issue with DST, with representations depending on whether T
is Sized?)


>
> -Erick
>
>
> On Saturday, July 27, 2013, Gábor Lehel wrote:
>>
>> Some discussion has sprouted on reddit:
>> http://www.reddit.com/r/rust/comments/1j5vbn/rustdev_rfc_removing_t/
>>
>> On Sat, Jul 27, 2013 at 4:51 PM, Gábor Lehel  wrote:
>>
>> Spurred by https://github.com/mozilla/rust/issues/7694 I was thinking
>> about the differences between `*` and `&` and the reason both need to exist.
>>
>> As far as I can tell the differences boil down to:
>>
>>   - The compiler makes no attempt to enforce any invariants for `*T`
>>
>> Meaning that, in an interesting duality-ish:
>>
>>  - Creating `*T`  out of `&T`  (or anything else) and manipulating it in
>> any which way is always safe
>>  - Dereferencing `*T` is unsafe
>>  - Creating `&T` out of `*T` is unsafe
>>  - Dereferencing `&T` (and whatever else the language lets you do with it)
>> is safe
>>
>> Behind it is proof obligations. `*T` has no implied invariants and
>> therefore doesn't require proof of anything, while as long as you stick to
>> `&T`, safety is proved by the compiler. It's at the boundary where the
>> burden is on the programmer: to assert (with an `unsafe` block) that the
>> invariants required for dereferencing `*T` and/or converting it to `&T`
>> really do hold.
>>
>> The use case for `*T` is operations which are not known to respect
>> invariants: notably foreign functions, also e.g. pointer arithmetic.
>>
>> The invariants required of `&T` but not `*T` are:
>>
>>   1. The pointer is not null
>>   2. The pointed-to object is of type `T`
>>   3. The pointed-to object is alive and initialized
>>   4. (Im)mutability and aliasing related invariants
>>
>> The latter three of which are guaranteed for the lifetime as

Re: [rust-dev] RFC: Removing *T

2013-07-29 Thread Gábor Lehel
On Sun, Jul 28, 2013 at 11:54 PM, Daniel Micay  wrote:
> On Sun, Jul 28, 2013 at 4:54 PM, Brian Anderson  wrote:
>> FWIW I prefer the terms box and reference. I don't really understand the
>> idea that only * is 'semantically a pointer' though.
>
> Unique boxes can be implemented without a pointer as long as the type
> is no larger than the slot needed to contain a pointer-size object.
> The pointer itself just isn't part of the type's contract, since
> there's no pointer arithmetic.

Hmm. Given immutability, in the absence of Cell or destructors, ditto
for &T and @T? Do we even specify that it will be pointer-sized?

Kind of amusing: you could have @@~~@@~~int with the same
representation as int, and "dereferencing" at each level just
transmute(self).

It may look like this is only of theoretical interest, because no one
in their right mind would pass int by pointer, but it would be a
useful optimization for generic code - for the same reason.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Removing *T

2013-07-29 Thread Gábor Lehel
On Mon, Jul 29, 2013 at 7:21 PM, Thiez  wrote:
> This would make for some interesting/confusing calling conventions. It would
> also mean &T and &mut T would no longer share a representation; &int would
> simply be int, but &mut int would still have to be a pointer.

Yes.

> Borrowing &mut T to &T would be a dereference if T is pointer size or smaller?

Yes. I would expect borrowing and never dereferencing is uncommon, but
of course possible.

> The only
> reliable way of taking an address would be taking &mut because & would fail
> for some types.

If by "taking an address" you mean transmute::<&T, *T>(&foo), then
yes. But that's the same as the next point.

You could have an addressof() which returns *T directly, instead of
going through &T.

> Transmuting *something to &something or back would fail for
> some types, unless we make transmute smarter.

Yes. That's what decoupling the semantics of a type from its
representation means.

Unsafe code would have to be more careful. Maybe there would be
special functions to deal with some things.

It would mean there would be no way /at all/, for some T, to go from
an &T to a *T pointing at the object it was borrowed from. You could
point it at the &T, but that might have a shorter lifetime. Not clear
if this would be problematic anywhere. (It would have be something
like calling a C function with a pointer, where the C function not
only accesses the pointer but returns or stores it (but doesn't mutate
through it), for a lifetime that's shorter than the original T's
lifetime but longer than the &T's.)

>
> I think for our sanity it would be best to let LLVM perform this kind of
> magic when it figures out we won't notice the difference.

(Can it?)

Anyway, this wasn't meant to be a proposal. Just exploring an idea.
It's fun to think about. Obviously there would be both complications
and benefits. But it definitely seems like a good idea to think
through what representation details should be specified on top of the
observable semantics, and what should be left up to the
implementation. *Reserving the right* to do this kind of thing might
be worthwhile (like we already do for enums).

> On Mon, Jul 29, 2013 at 4:47 PM, Gábor Lehel  wrote:
>>
>> On Sun, Jul 28, 2013 at 11:54 PM, Daniel Micay 
>> wrote:
>> > On Sun, Jul 28, 2013 at 4:54 PM, Brian Anderson 
>> > wrote:
>> >> FWIW I prefer the terms box and reference. I don't really understand
>> >> the
>> >> idea that only * is 'semantically a pointer' though.
>> >
>> > Unique boxes can be implemented without a pointer as long as the type
>> > is no larger than the slot needed to contain a pointer-size object.
>> > The pointer itself just isn't part of the type's contract, since
>> > there's no pointer arithmetic.
>>
>> Hmm. Given immutability, in the absence of Cell or destructors, ditto
>> for &T and @T? Do we even specify that it will be pointer-sized?
>>
>> Kind of amusing: you could have &&&&@@~~@@~~int with the same
>> representation as int, and "dereferencing" at each level just
>> transmute(self).
>>
>> It may look like this is only of theoretical interest, because no one
>> in their right mind would pass int by pointer, but it would be a
>> useful optimization for generic code - for the same reason.
>>
>> --
>> Your ship was destroyed in a monadic eruption.
>> ___
>> Rust-dev mailing list
>> Rust-dev@mozilla.org
>> https://mail.mozilla.org/listinfo/rust-dev
>
>
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Java versus .NET style for acronyms in type names

2013-08-05 Thread Gábor Lehel
I think I prefer .NET style. I liked the mixed approach, until I saw
it applied to GC/GcMut and co. I'm ambivalent about grandfathering in
IO: consistency is good, but Io /really does/ look unusually stupid,
with other acronyms it's not as ingrained for whatever reason. (I
wonder whether it has anything to do at a subconscious level with the
fact that, in Input/Output, the two words are on the same level and
independent, whereas in e.g. "garbage collected", the latter depends
on the former... or whether it's just frequency of exposure.)

The one case where I don't like .NET is when an acronym also forms a
familiar word, i.e. Arc. The solution there might just be to rename it
to AtomicRc. (That would /also/ be helpful to avoid confusion with
Apple's ARC.)

On Sat, Aug 3, 2013 at 3:28 AM, Patrick Walton  wrote:
> Hi everyone,
>
> Brendan Eich emailed me expressing a preference for `GC<>` over `Gc<>`. I
> think now is as good a time as any to have the bikeshedding debate :)
>
> I've noticed two styles for acronyms in type names: Java style (HTTPServer)
> versus .NET style (HttpServer). Currently we are usually using .NET style,
> but inconsistently (e.g. ARC). We never really decided.
>
> Here are a few examples of types in each style:
>
> * Java style: GC, ARC, SimpleHTTPServer, XMLHTTPRequest.
>
> * .NET style: Gc, Arc, SimpleHttpServer, XmlHttpRequest.
>
> I slightly prefer Java style myself because I think "GC" looks better than
> "Gc", because Web APIs use Java style, and because Python does (e.g.
> SimpleHTTPServer) and in general we've been following PEP 8. But I don't
> feel strongly on this issue.
>
> Thoughts/straw poll?
>
> Patrick
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Overloadable dereference operator

2013-08-05 Thread Gábor Lehel
I agree that overloadable * would be very desirable for smart
pointers. I also really like the interaction with newtypes. Is it
still the plan (from the GC thread) to push all mutability into Cell?
Would that interact with this proposal in any way (e.g. to allow
mutating through * in some cases)?

The method lookup part of it feels too magical. What if both the
pointer and the pointee have a method with the same name? In general,
how can you tell whether you're calling a method on the "pointer" or
the "pointee"? The reason using the dot both for calling methods
directly and for calling them through pointers worked so far is that
pointers didn't have methods. Disambiguating this situation is what
C++ has the -> operator for: would it be so bad to adopt it? With a
simple desugaring to (*foo).bar it would remove the magic, and the
common fields use case would also continue to work with a different
syntax. I'm guessing this is another of those decisions that was
discussed and settled a long time ago. Is there any other language
with universal dot notation where custom smart pointers are common?

On Tue, Jul 30, 2013 at 7:28 AM, Patrick Walton  wrote:
> Currently, newtype structs automatically dereference to the value they
> contain; for example:
>
> struct MyInt(int);
> fn main() {
> let x = MyInt(3);
> printfln("1 + 2 = " + x.to_str()); // prints "1 + 2 = 3"
> }
>
> This behavior is sometimes undesirable, as Brian often points out. Haskell
> allows behavior similar to this to be controlled on an opt-in basis with
> `GeneralizedNewtypeDeriving`.

I think these are subtly different. Rust is automatically unwrapping
the newtype when calling a method on it, while Haskell is
automatically generating requested trait impls based on the ones for
the inner type. But not vice versa: for functions other than those on
derived traits/classes Haskell still requires manual unwrapping, and
Rust doesn't consider the newtype to impl traits just because the
inner type does. Allowing overloads of */-> on newtypes and
GeneralizedNewtypeDeriving would be independently useful features
(just be careful when mixing the latter with associated types).

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Trait method self parameter type clashes with lifetime annotation required by the implementation

2013-09-29 Thread Gábor Lehel
'self is not special in any way, except that the compiler has historical
baggage such that 'self is the only name it lets you use for a lifetime
parameter on a struct. But that's a bug. In the ideal platonic Rust in the
sky, you can have any number of lifetime parameters on a struct with
whatever names you prefer.

The way I've found to think about lifetimes is that if you have:

r_int: &'a int

then 'a refers to a span of time (a scope, a lifetime) such that
lifetime_of(r_int) <= 'a <= lifetime_of(*r_int). (Where *r_int is intended
metaphorically to refer to the original object r_int was created from, not
the result of the *r_int expression itself.) So 'a is a kind of stand
between to ensure that r_int does not outlive the object it refers to.

If you have

fn foo<'a>(r_int: &'a int) -> &'a int

then just like any other generics argument, the lifetime 'a is chosen by
the caller of `foo` (as inferred by the compiler). Typically the caller
will have an int object (i: int), then borrow a reference to it (r_int: &'a
int = &i) which it passes to `foo`, and then 'a will be the lifetime of the
int. `foo` will then have to return a reference to (an int that lives at
least as long). In practice this could either be the r_int it got as
argument, or a static int.

`fn foo(arg: &int)` is shorthand for an anonymous lifetime parameter:
`fn foo<'a>(arg: &'a int)`

In the return type position `fn foo() -> &int` is short for `fn foo<'a>()
-> &'a int`, meaning `foo` has to return a reference to (an int that lives
as long as any lifetime the caller could choose), which in practice means
that it has to be 'static. I believe you are or will be required to write
&'static explicitly in these cases to avoid confusion.

With a struct it's not much different.

s: MyStruct<'a> means lifetime_of(s) <= 'a
s: MyStruct<'a, 'b> means lifetime_of(s) <= 'a && lifetime_of(s) <= 'b

If you have

struct MyStruct<'self> {
r_int: &'self int
}

s: MyStruct<'a>

then lifetime_of(s) <= 'a && lifetime_of(s.r_int) <= 'a. (Which is trivial
because lifetime_of(s) == lifetime_of(s.r_int).)

Basically, every object has a lifetime - from its creation to its
destruction - but a lifetime parameter or argument typically refers to the
lifetime of something else, which the object itself must not or does not
outlive.

(Please yell at me if I got any of this wrong.)



On Sun, Sep 29, 2013 at 5:23 PM, Oren Ben-Kiki  wrote:

> Ok, color me confused... perhaps there's somewhere that explains 'self on
> more detail? For example, _why_ does the example below not work without the
> explicit <'self>? It seems like it should.
>
> I have yet to truly understand the whole 'self thing. When I first read
> about lifetimes, my naive expectations were that:
>
> - Every struct has a 'self lifetime, which is basically "as long as this
> struct exists". It doesn't matter if I have a @ of the struct or a ~ of the
> struct or just a local variable with the struct... when the struct is
> dropped, the lifetime ends.
>
> - It follows there's no need to ever annotate structs as generic with a
> <'self> parameter - it always exists.
>
> - Any & in a struct is either &'self or &'static. A simple & should be
> &'self as that makes more sense (but if Rust wants me to be explicit, fine).
>
> This were my "least surprise" expectations, but things don't work this
> way... the problem is I don't have a simple mental model to replace the
> above with, so I struggle. What _is_ 'self, exactly?
>
> Isn't a function fn foo(&self) -> &T the same as returning a &'self T? Why
> would I want to say fn foo<'a>(&'a self) in the 1st place - 'a is "by
> definition" the same as 'self? How come David's Foo example fails the
> borrow check?
>
> Besides failing (my) "least surprise" expectations, the current rules also
> seem to be a "leaky abstraction". If I have a struct that holds a ComplexT
> member, it needs no <'self> parameter. If I then add a private member to my
> struct to hold some &'self PartOfT (say, cached access to an internal
> member), then boom, all uses of my struct now have to say <'self>, I can no
> longer put it in thread-local-storage, etc. I'd expect keeping these sort
> of cached borrowed pointers should be an internal implementation detail
> which does not affect the users of the struct at all.
>
> I suppose there's a good reason for all this, and a reasonable mental
> model I need to put in my head, but digging around the docs I didn't find
> one... Any hints would be appreciated :-)
>
>
> On Sun, Sep 29, 2013 at 5:42 PM, David Renshaw wrote:
>
>> Cool! I think that solution is much better than mine.
>>
>> > But I
>> > think that polluting traits-interfaces with lifetime annotation is
>> > wrong. Why the trait should have lifetime annotation? It is
>> > implementation detail.
>>
>> Just in case you want to see a case where it *does* make sense to put
>> a 'self lifetime in a trait definition, here is an example:
>>
>> https://gist.github.com/dwrensha/db919b8e130e9eb72f0f
>> _

Re: [rust-dev] Trait method self parameter type clashes with lifetime annotation required by the implementation

2013-09-29 Thread Gábor Lehel
`On Sun, Sep 29, 2013 at 9:21 PM, Oren Ben-Kiki  wrote:

> Thanks for the explanation. You said two key points:
> - Basically, every object has a lifetime - from its creation to its
> destruction - but a lifetime parameter or argument typically refers to the
> lifetime of something else, which the object itself must not or does not
> outlive.
> And:
> - 'self is not special in any way, except that the compiler has historical
> baggage such that 'self is the only name it lets you use for a lifetime
> parameter on a struct.
>
> So, 'self is indeed very far from what I thought (hoped) it would be.
>
> Taking these together, do I read this right as saying there is no way
> whatsoever to say:
>
> struct Foo {
> bar: Bar,
> baz: &'i-live-as-long-as-the-foo-struct-and-no-more BazPartOfBar,
> }
>

Per my understanding, this is correct. Because there is a constraint on the
lifetime of a part of `Foo`, there must a constraint on the lifetime of
`Foo`. It has to propagate outwards to make sure the lifetime of the whole
structure is properly constrained. You basically want to "propagate
inwards". I don't think that's possible, but maybe someone will correct me.


>
> When writing a non-trivial container, I found several user cases to be
> extremely problematic. One was the above; a container held a "spine" or
> "master" or "owned" or whatever-you-want-to-call-it data structure(s), plus
> borrowed pointers that only live as long as the container and allow quick
> access to specific parts of it.
>
> Is this impossible in Rust (barring use of @ or unsafe pointers)?
>

This sounds similar to the case of a doubly linked list (with forward
pointers being the "spine" and backwards the "quick access"), which is  not
possible as an 'owned' structure as far as I know without unsafe pointers.


>
>
> On Sun, Sep 29, 2013 at 8:24 PM, Gábor Lehel  wrote:
>
>> 'self is not special in any way, except that the compiler has historical
>> baggage such that 'self is the only name it lets you use for a lifetime
>> parameter on a struct. But that's a bug. In the ideal platonic Rust in the
>> sky, you can have any number of lifetime parameters on a struct with
>> whatever names you prefer.
>>
>> The way I've found to think about lifetimes is that if you have:
>>
>> r_int: &'a int
>>
>> then 'a refers to a span of time (a scope, a lifetime) such that
>> lifetime_of(r_int) <= 'a <= lifetime_of(*r_int). (Where *r_int is intended
>> metaphorically to refer to the original object r_int was created from, not
>> the result of the *r_int expression itself.) So 'a is a kind of stand
>> between to ensure that r_int does not outlive the object it refers to.
>>
>> If you have
>>
>> fn foo<'a>(r_int: &'a int) -> &'a int
>>
>> then just like any other generics argument, the lifetime 'a is chosen by
>> the caller of `foo` (as inferred by the compiler). Typically the caller
>> will have an int object (i: int), then borrow a reference to it (r_int: &'a
>> int = &i) which it passes to `foo`, and then 'a will be the lifetime of the
>> int. `foo` will then have to return a reference to (an int that lives at
>> least as long). In practice this could either be the r_int it got as
>> argument, or a static int.
>>
>> `fn foo(arg: &int)` is shorthand for an anonymous lifetime parameter:
>> `fn foo<'a>(arg: &'a int)`
>>
>> In the return type position `fn foo() -> &int` is short for `fn foo<'a>()
>> -> &'a int`, meaning `foo` has to return a reference to (an int that lives
>> as long as any lifetime the caller could choose), which in practice means
>> that it has to be 'static. I believe you are or will be required to write
>> &'static explicitly in these cases to avoid confusion.
>>
>> With a struct it's not much different.
>>
>> s: MyStruct<'a> means lifetime_of(s) <= 'a
>> s: MyStruct<'a, 'b> means lifetime_of(s) <= 'a && lifetime_of(s) <= 'b
>>
>> If you have
>>
>> struct MyStruct<'self> {
>> r_int: &'self int
>> }
>>
>> s: MyStruct<'a>
>>
>> then lifetime_of(s) <= 'a && lifetime_of(s.r_int) <= 'a. (Which is
>> trivial because lifetime_of(s) == lifetime_of(s.r_int).)
>>
>> Basically, every object has a lifetime - from its creation to its
>&g

Re: [rust-dev] Trait method self parameter type clashes with lifetime annotation required by the implementation

2013-09-30 Thread Gábor Lehel
On Mon, Sep 30, 2013 at 8:31 AM, Oren Ben-Kiki  wrote:

> Huh, this is _exactly_ my use case. I have data structures which I grow
> but never shrink, and I never move anything out of them. This idiom isn't
> that uncommon when writing in functional style...
>
> I incrementally build a complex structure that allows quick access to the
> same pieces using different criteria (e.g., access a piece by a unique id,
> or look it up by a path, or via a direct pointer held by a related piece,
> etc.). All accesses are of (possibly mutable) borrowed pointers that live
> as long as the whole thing exist. Then when done I can discard the whole
> thing.
>

Just a thought, but when you write "incrementally build a complex structure
... discard the whole thing", I think "arena allocation". That /might/
solve your borrowed pointer woes by letting you pervasively use borrowed
pointers which all live at-most-as-long-as the arena (and hence the
structure) itself. The borrow checker in my head is not advanced enough to
let me really think it through though. And I don't think the compiler will
let you send anything if there are borrowed pointers involved. There's an
`arena` module[1] in libextra, but I've never tried it, and it uses `@`
pointers internally which also precludes sending.

(I wonder if there's any way to safely encode the idea that if all pointers
are internal to the arena (there's nothing pointing in or out), then
sending an ~arena should be safe, because its location in memory doesn't
change. Presumably you would also need to move-send a pointer to the
beginning of the structure inside of the arena alongside it. Maybe borrowed
pointers are not the best fit here, or maybe they are, I dunno.)

[1]: http://static.rust-lang.org/doc/master/extra/arena/struct.Arena.html



>
> Sometimes I have a less-complex structure to which I attach a related
> "view" structure. The view holds borrowed pointers to pieces of the
> original immutable structure, allowing efficient access in new and
> interesting ways. When done I can then discard the view and keep the
> original.
>
> So basically, I want something that gives me the freedoms granted to
> &'static, but only for as long as the "main" structure provably exists.
> Something like &'shadow :-)
>
> I found Rust doesn't like these idioms at all. I some cases, where I was
> too tired, performance wasn't an issue, and there was no need to send the
> whole thing between tasks, I just used @ pointers. Otherwise, I used RcMut,
> though this incurs boilerplate access code and hurts performance for no
> "real" reason. In critical cases I may end up using unsafe pointers...
>
> Using a macro - hmm. Interesting and if possible, would be a great
> solution. I'm not certain what such a macro would expand to, though. It
> would need to be something that would express the concept of "I live only
> as long as my container structure" _somehow_, and I thought we established
> that can't be done...
>
> Thanks,
>
> Oren.
>
> On Mon, Sep 30, 2013 at 4:26 AM, Steven Blenkinsop wrote:
>
>> Yeah, I was trying to come up with a design a while ago to allow
>> intrastructural borrowed pointers, and basically the effect this had was
>> that you could never move anything out of the datastructure since that
>> would leave dangling pointers. Which means you could grow the structure but
>> never shrink it, which is sort of not good.
>>
>> Now my thought is about whether it would be possible to make a macro
>> which allows you to define a safely encapsulated smart node which ensures
>> that any intrastructural references meet certain invariants about where
>> they point within the structure, but I haven't developed the idea enough to
>> say whether you could make something truly general this way.
>>
>>
>> On Sun, Sep 29, 2013 at 7:28 PM, Steven Fackler 
>> wrote:
>>
>>> Foo can't really be used safely. Say that we have
>>>
>>> struct Bar {
>>> baz: BazPartOfBar
>>> }
>>>
>>> struct Foo {
>>> bar: Bar,
>>> baz: &'magic BazPartOfBar
>>> }
>>>
>>> And let's say we add a `self` syntax to allow field initializers to
>>> refer to other fields:
>>>
>>> let foo = Foo {
>>> bar: Bar {
>>> baz: BazPartOfBar
>>> },
>>> baz: &self.bar.baz
>>> };
>>>
>>> We can't really do much with Foo. If we move it, foo.baz is no longer a
>>> valid reference, so that can't happen. We could modify foo.bar in this
>>> case, but not if Bar were defined as
>>>
>>> struct Bar {
>>> baz: ~BazPartOfBar
>>> }
>>>
>>> since foo.baz would point to deallocated memory if we replace self.bar
>>> or self.bar.baz.
>>>
>>> Steven Fackler
>>>
>>>
>>> On Sun, Sep 29, 2013 at 3:15 PM, Tim Kuehn  wrote:
>>>
 Could you use struct methods for "quick access"? Or is there a reason
 this wouldn't fit your use case? Sorry, I haven't followed the whole thread
 closely.

 struct Owner {
 owned: ~[int],
 }

 impl Owner {
 fn quick_access<'a>(&'a mut self) -> &'a mut int {
  

[rust-dev] Unified function/method call syntax and further simplification

2013-10-18 Thread Gábor Lehel
Hi list,

This is meant as a followup to an earlier thread[1] on the subject and the
related ticket[2].

[1]: http://thread.gmane.org/gmane.comp.lang.rust.devel/2622/
[2]: https://github.com/mozilla/rust/issues/6974

The idea in those earlier discussions is that methods could also be called
using function syntax, supplying `self` as the first argument, so instead
of `self_arg.method(arg)`, you could write `method(self_arg, arg)`. I'm
wondering if this could be taken a couple steps further to simplify the
whole story regarding functions, methods, traits, and impls. The idea is
that the distiction between functions and methods would be erased almost
completely, and methods (now being just functions) would be imported
explicitly.

It would involve the following pieces:

 - If the first argument of a top-level `fn` is named `self`, it can be
called using method syntax. So if you have `fn foo(self: &MyType, n: int)
{ .. }` at the top level, you can write `object_of_my_type.foo(123)`. You
can also still call it using function syntax: `foo(object_of_my_type, 123)`.

 - Anonymous `impl`s could then be removed in favor of such top-level
`fn`s. Alternately, they could be kept as syntactic sugar for the same
thing.

 - Just as the distinction between functions and anonymous-impl methods
would be erased, the distinction between trait methods and associated
functions would also be erased. A function declared inside a trait would
exist in module scope, just like top-level `fn`s. Just like other
functions, you could call it using method syntax if the first argument is
named `self`, or using function syntax either way.

 - The existing syntax for `self` arguments could then be removed.
Alternately, it could be kept as syntactic sugar (`?self` becoming `self:
?T`, where `?` is any sigil or none).

 - Now that everything is a top-level `fn`, they would be imported
explicitly, like they (and all other things) already are. Here we can
remove the restriction on methods needing to be declared in the same
module/crate as their `self` type.

 - Functions can be called using method syntax only if they are declared
in, or imported into, the current scope directly. In other words, if you
could call them using function syntax unqualified.

 - If a method call is ambiguous, you would have the following options to
resolve it:

   (a) Tweak your imports so that only the method you want is in scope.

   (b) Import the method under a different name instead.

   (c) Use function call syntax instead with an explicit module qualifier.

Again, more or less the same options you have with any non-method item in
the current language.


Disadvantages of this scheme:

 - More typing. (Explicit imports for one; if sugar is removed, then that
as well.)


Advantages:

 - Simplicity and transparency. The special rules and language constructs
for declaring and importing methods would go away. Methods would work the
same way as other things do.

 - Correspondingly, you would also have better control over which methods
are imported and invoked.

 - You could declare methods for any type in any crate or module, without
having to create extraneous traits, which I feel is a wart in the current
language. Traits could then be used for abstraction only, which I feel is
their correct purpose.

 - Because methods would just be module-scope functions, they could be
referred to as values (e.g. as arguments to higher-order functions) without
a lambda shim.

 - I was confused at one point because `&self` looks a lot like a pattern
match, but is not one. That could go away.

 - The current difficulties with generic methods (`impl T { .. }`
in the current language) might, or might not, be reduced. (I don't know of
any similar issues with generic top-level functions, but they might
resurface if called as methods).

 - The significance of the argument named `self` could also potentially be
removed, instead allowing the first argument to be used as the
self-argument in a method call, no matter what it's called. This would
allow for pattern matching on the self-argument in the definition of the
method, which is currently not possible.


Hope this is helpful or interesting,
Gábor

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] mutable vs. functional APIs

2013-10-18 Thread Gábor Lehel
On Fri, Oct 18, 2013 at 6:09 PM, Jack Moffitt  wrote:

> If we decide that both API styles are good to have, what should the
> naming convention be for the functional vs. mutable ones? Ruby,
> Scheme, and Clojure use `!` to denote the in-place mutation ones, but
> that syntax is for macros in rust.
>

FWIW, the convention in many other APIs is that the return-modified-copy
versions use past participle. So e.g. the modify-in-place method would be
called `foo.frobulate()`, while the "functional" one would be called
`foo.frobulated()`. I think this is nice, but it can get a bit more
difficult with arguments. (If the mutating method is called
`foo.append(bar)`, what's the other one? `foo.appended(bar)`?
`foo.with_appended(bar)`? Something else?)

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Unified function/method call syntax and further simplification

2013-10-19 Thread Gábor Lehel
On Sat, Oct 19, 2013 at 4:08 PM, Oren Ben-Kiki  wrote:

>
> I'm less certain about giving up `impl Foo { ... }`, though - that is
> useful for logically grouping, documenting and accessing functions (as in
> `Foo::foo(...)`). But it seems we don't have to give it up, just make it
> optional?
>

Interesting point. I think this argues in favor of removing anonymous
`impl`s, even as sugar. I was focused on normal ("selfish") methods in my
previous letter, rather than "static" ("selfless") ones. You could still
achieve much the same effect, if you wanted to, by writing:

struct Foo { .. }

fn get_thing(self: &Foo) -> Bar { .. } // for `some_foo.get_thing()`
syntax

mod Foo {
fn new() -> Foo { .. } // for `Foo::new()` syntax
}

Rust already allows this trickery: the type and module are imported
together when you write `use some_mod::Foo`. Keeping this seems fine, and
making it more explicit (relative to `impl`) seems like a good thing. What
you lose is that in the case of a generic type, you would have to write the
generic-bits for each function, which is the "more typing" disadvantage
from earlier. I think losing the `impl` sugar is a small price to pay for
the simplification of the language. Modules would be modules and types
would be types. Append to the "advantages" list:

 - The complexity related to (possibly-generic) types in paths would go away


On Sat, Oct 19, 2013 at 3:47 PM, Matthieu Monrocq <
> matthieu.monr...@gmail.com> wrote:
>
>> I see no reason for the restriction of "self". Why not simply say that
>> any function can be called with "first_arg.func(...)" style ?
>>
>
Yeah, as I mentioned in the last bullet of the previous letter, you could
do this. I don't think it makes much difference either way and don't have a
strong opinion.

In favor of explicit `self` is that it's, well, explicit: "you can and
should call this using method syntax". I can imagine there might be
function signatures where using method syntax wouldn't make sense (though
you might say, "then don't use it", and I would be inclined to agree). A
non-method function (one without a `self` arg) also wouldn't cause
ambiguity if it were in scope at the same time as you were calling another
method with the same name.

In favor of always allowing method syntax on the first argument is that you
could destructure it in the definition, e.g. `fn area((x, y): (int, int))
-> int { x * y }`, and still call the function using method syntax.

They're all pretty minor.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Unified function/method call syntax and further simplification

2013-10-20 Thread Gábor Lehel
On Sat, Oct 19, 2013 at 10:52 PM, Patrick Walton wrote:

> I think it's unfortunately too late to overhaul the language like this.
> This will require redesigns of all Rust code in existence.
>
> I do like unified function/method call syntax, but I think it can be done
> in a backwards compatible way.
>

This is up to you and the rest of the Rust team, of course. But in my very
humble opinion, it feels shortsighted to impose backwards compatibility
constraints at this point. All Rust code in existence is still (hopefully!)
a negligibly small fraction of the code that is yet to be written. I know
there's a desire to get 1.0 out the door as soon as possible, and I
understand it (I want to be using Rust instead of C++, too!), but if Rust
is still in use years or decades from now, decisions made now about the
design of the language will echo far louder than whether it was released a
few months earlier or later. I think it would be unfortunate to start
suffering a C++-like fate sooner than absolutely necessary.

That was a general argument, and it only matters if something is considered
a good idea on the merits. In this case, it seems we disagree about that:

On Sat, Oct 19, 2013 at 11:46 PM, Patrick Walton wrote:

> I was thinking not. The dot operator should still have special name lookup
> rules: it searches through associated impls and all traits in scope. It
> cannot call anything that is not attached to an impl.
>
> This does make functions and methods somewhat less unified, but it makes
> methods feel more like OO methods from a scoping POV. I feel the draws of
> methods are not only that they switch the order of the receiver and action
> but also that they allow functions associated with a type to be called
> without explicitly importing their names.
>

Aha. That's exactly the thing I don't like, and thought would be beneficial
to change. It's different from everything else for no great reason (is
being like OO a great reason?), and carries baggage with warts and thorny
issues in it. (Needing special language constructs to write methods; normal
`fn`s feel like second-class citizens; can't selectively or rename-import
methods; can't write method-syntax functions in a different crate without
introducing spurious abstraction boundaries; the whole `impl T { fn(&self)
}` versus `impl &T { fn(self) }` ugliness; traits and generic types in
paths raise awkward questions; ...)

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Unified function/method call syntax and further simplification

2013-10-20 Thread Gábor Lehel
On Sun, Oct 20, 2013 at 4:56 PM, Patrick Walton  wrote:

> I don't see the things you mention as warts. They're just consequences of,
> well, having methods in the OO sense. Nearly all of these "warts" show up
> in other object-oriented languages too. Maybe they're warts of
> object-oriented programming in general and illustrate that OO is a bad
> idea, but as I mentioned before Rust is designed to support OO.
>

OO for me was always more tied in with virtual methods than with how
methods are scoped. But either way - I think this is basically my view. :)
The only part of it I like is dot syntax.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Unified function/method call syntax and further simplification

2013-10-20 Thread Gábor Lehel
On Sun, Oct 20, 2013 at 5:11 PM, Marijn Haverbeke  wrote:

> Another problem with this proposal seems that it does away with the
> possibility of explicitly grouping a bunch of methods that make up the
> implementation of an interface. Implementing interfaces go-style, by
> just happening to have implemented all the methods that make up the
> interface, seems inappropriate for a language where interfaces aren't
> structural.
>

No, I completely agree with you here. Traits are awesome and if anyone
wants to remove them, I will fight them with forks. The only difference in
my proposal is how their methods would be scoped: they would be under the
enclosing module, rather than the trait itself being a kind of module.
Basically the Haskell model. (Except Haskell's module system is weaker than
Rust's.)


So I very much agree with Patrick. Some aspects of this proposal are
> attractive, but it breaks some essential properties of the way methods
> currently work (and probably can't be adjusted to work around that
> without losing most of it attraction).
>

The main cost would be having to import methods explicitly (or using a
glob), as Patrick notes.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Unified function/method call syntax and further simplification

2013-10-20 Thread Gábor Lehel
On Sun, Oct 20, 2013 at 7:31 PM, Gábor Lehel  wrote:

> So I very much agree with Patrick. Some aspects of this proposal are
>> attractive, but it breaks some essential properties of the way methods
>> currently work (and probably can't be adjusted to work around that
>> without losing most of it attraction).
>>
>
> The main cost would be having to import methods explicitly (or using a
> glob), as Patrick notes.
>

Now I'm wondering, though: going with my proposal, if the module structure
were just a little bit more fine-grained, I think you could achieve
basically the same effect as the current system, if you wanted to. If the
convention were to use a module to enclose a type together with its methods
(which is already the case in many places), and you did a glob import of
that module, you would get what you have now: the type and its methods
would all come into scope. Except you could still import selectively, if
you wanted to, or do things any other way, if you wanted to, and all of the
"warts" I mentioned would still disappear. In a different crate, you could
similarly create a module of "extension methods", which could be imported
together or selectively, if you wanted to.

I don't want to push this too hard if it's past the point of being
productive, but I'm now even more convinced that this would be a beneficial
change. Types should be types, grouping and scoping should be done with
modules (Rust's modules are very capable), and traits should be used for
type-based abstraction. I think it would work great.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Unified function/method call syntax and further simplification

2013-10-20 Thread Gábor Lehel
On Sun, Oct 20, 2013 at 8:01 PM, Patrick Walton  wrote:

> What would happen if two types defined a method called, say, "foo", and
> the importing module glob imported them both?
>

Here's the part where I have to say that I'm not intimately familiar with
how Rust's existing method lookup works. But I don't see why it would
necessarily have to work any differently from the way it does now. If you
use dot syntax, it would try to do type-based name resolution (i.e. use the
method in scope whose self-type matches), autoborrowing, autoreferencing,
and whatever else it currently does to try to get an unambiguous match. If
that fails, *then* you would have the option of tweaking your imports,
`use`ing the method under a different name, or switching to function syntax
and adding an explicit module qualifier (along with explicit borrowing and
whatever).

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Unified function/method call syntax and further simplification

2013-10-20 Thread Gábor Lehel
On Sun, Oct 20, 2013 at 7:31 PM, Gábor Lehel  wrote:

> On Sun, Oct 20, 2013 at 5:11 PM, Marijn Haverbeke wrote:
>
>> Another problem with this proposal seems that it does away with the
>> possibility of explicitly grouping a bunch of methods that make up the
>> implementation of an interface. Implementing interfaces go-style, by
>> just happening to have implemented all the methods that make up the
>> interface, seems inappropriate for a language where interfaces aren't
>> structural.
>>
>
> No, I completely agree with you here. Traits are awesome and if anyone
> wants to remove them, I will fight them with forks. The only difference in
> my proposal is how their methods would be scoped: they would be under the
> enclosing module, rather than the trait itself being a kind of module.
> Basically the Haskell model. (Except Haskell's module system is weaker than
> Rust's.)
>

Sorry, I think I might have misread the question, which I now believe was
not about traits, but about impls. Basically, only anonymous `impl`s would
go away. Trait impls would stay. But trait impls wouldn't have an effect on
scoping. (Trait methods would be scoped under the module enclosing the
trait, `impl`s would only affect for which types there exists an
implementation for them. You can' t currently choose whether or not to
export or import a trait impl, and you would continue to not be able to do
that.)

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Unified function/method call syntax and further simplification

2013-10-20 Thread Gábor Lehel
On Sun, Oct 20, 2013 at 8:16 PM, Patrick Walton  wrote:

> But then it seems strange to require that the methods be imported at all,
> if you're going to do single-dispatch type-based resolution on them anyway.
>

Well, it would do type-based resolution based on the methods which you
import :). You would still be the one doing the importing, instead of it
happening by magic. You could also /not/ import one or more of them, if you
wanted to, which you can currently only do on a per-trait granularity. And
the rest of the design would still be much cleaner. (Again, leaving to
modules what can be done with modules.)

But FWIW, I think this is mostly orthogonal. If you use dot syntax, method
lookup happens [somehow]. If it fails, you can do the other things. Maybe
method lookup happens with type-based resolution, as it does now. Or maybe
it requires that the name itself be unambiguous (which is simpler and
dumber), as with functions. That's not really the part I care about. The
part I care about is the separation of concerns.



> What we could do, perhaps, is add a third source of methods that the dot
> operator searches: functions in scope. This would allow methods to be
> attached anywhere to pre-existing types in an ad-hoc basis. I'm a bit
> nervous about complicating our already-complicated method lookup further,
> but maybe it makes sense to reduce the number of FooMethods traits people
> have to write, as well as to accommodate function chaining patterns.
>

I still can't speak to the method resolution code :), but I think this
would be a positive step. (Requiring the explicit `self` argument would
probably make sense, if this were to happen.)

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Unified function/method call syntax and further simplification

2013-10-20 Thread Gábor Lehel
On Sun, Oct 20, 2013 at 9:38 PM, Patrick Walton  wrote:

> I guess I just don't see the value in requiring imports of names when
> there can be no ambiguity and they're defined in one place (the impl of the
> type, defined in the same place as the type itself). One person's "magic"
> is another person's "smart compiler".
>

A big part of the motivation would be to allow declaring methods anywhere,
without using traits, and then "they're defined in one place" stops being
true. For that to be workable you need to have control over imports.
(Luckily with top-level functions, you already do.)



> I'm not convinced that this is solving a real problem.
>

It's not "solving a real problem" so much as simplifying the whole thing.
Despite having used the word, my beef with the current system is not so
much that it's magical. It's that it's completely unnecessary. With nothing
more than a simple syntax rule and the existing module system, you can
recover all of the expressive power of the current system, and more.

Compare:


-- Design 1 --

 - You can declare functions using `fn`.

 - Functions in the current scope can be called using dot syntax with the
first argument as the receiver. (Optionally: only if the first argument is
named `self`.)

 - The module system can be used to group types, functions, and other
items, import them together, selectively, or under different names, and
resolve ambiguities between them.


-- Design 2 --

 - Anonymous `impl` blocks can be used to associate methods with a type.

 - Methods are scoped under and imported together with their type or trait.

 - Because of this, an anonymous `impl` can only be declared in the same
module as its type.

 - If you want to declare a method somewhere else, declare an auxiliary
trait and implement it for that type.

 - For a method to be called with dot syntax, it has to be declared using
special `self`, `&self`, `~self`, (or so forth) syntax, which only works
with the built-in pointer types. Such a method can't be called with
function syntax.

 - If you want a self-type other than one of the built-in pointers, write
an `impl` specifically for that type.

 - Types and traits are like modules in some ways (you can use them in a
path), but not others (you can't `use` from them).

 - There are special rules and/or syntax for dealing with traits, generic
types, and optionally their type arguments in paths.

 - In addition to methods, you can also declare top-level functions with
`fn`. These can only be called with function syntax.

 - The module system can be used to group, import, rename, and resolve
ambiguities between types, functions, and other items except for methods.

Which one feels like the tighter design?

With some extensions (methods can be called with function syntax, in-scope
functions are also considered during method lookup), Design 2 could close
the expressiveness gap versus Design 1. But the simplicity gap would remain.



> The "search all traits in scope" behavior strikes me as far more magical
> than this.
>

All of that said, maybe I'm insufficiently worked up about the method
lookup rules. :) Searching trait impls does sound a little bit scary. What
about, as a less scary in-between solution, while not throwing the baby out
with the bathwater, attempting to unify the type of the receiver with the
types of the first (`self`) arguments of the functions of the given name in
the current scope, but *not* looking at trait impls? And if there's a
unique match (modulo autoborrowing and such) it's selected, otherwise an
error is reported. Whether appropriate trait impls exist would be checked
after a method is selected. This means that if you have a trait method or
function in scope which is generic in its self-type, and any other function
which also matches the type of the receiver, it would lead to ambiguity.
But as (in my proposal) you would no longer have to use traits to implement
extension methods, this would hopefully be a less common occurrence.

Again though, I think this can and should be considered as a separate
matter. It could be done inside the current system, I think, if you wanted
to. The differences in the context of my proposal are the aforementioned
not having to use traits for extension methods, and that you would have
more ways to resolve an ambiguity.


It's probably easier to list languages that *don't* have this feature in
> some form than to list languages that do, honestly...
>

That's because all of the mainstream languages follow misguided OO ideas,
and most of the others copy them. :-)


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] State of private

2013-11-09 Thread Gábor Lehel
On Sat, Nov 9, 2013 at 7:43 AM, Oren Ben-Kiki  wrote:

> Many thanks for the replies.
>
> My problem is actually in accessing private methods/members of a struct
> defined in a different, but "very closely related" module. It seems @
> nikomatsakis  is saying in the final
> text comment of https://github.com/mozilla/rust/issues/8215 that it is
> not possible to specify methods/members that are only accessible from
> within a small set of modules; they are either completely public, or
> private to a single module.
>

FWIW, I think this might also be partially addressed by my earlier proposal
to remove methods as a distinct concept, and just allow using dot-syntax
with any function? (I have a draft of a follow-up to that thread that's
been sitting there for two weeks... sigh.)

It wouldn't help with struct members though, which is interesting. Maybe
the import syntax could be extended to accomodate that?

E.g.

mod a { pub struct Point { x: int, y: int } }

mod b { use a::Point; } // currently exists, import all fields

mod c { use a::Point { * }; } // also import all fields

mod d { use a::Point { }; } // import no fields

mod e { use a::Point { x }; } // import only x, not y

Then you could play similar games with struct fields as with items.

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Please simplify the syntax for Great Justice

2013-11-12 Thread Gábor Lehel
Does anyone have empirical data (or even anecdotes) about whether or not
C++ hackers find Rust's syntax appealing? :-)


On Tue, Nov 12, 2013 at 10:53 AM, spir  wrote:

> On 11/11/2013 09:46 PM, Corey Richardson wrote:
>
>> I don't think Rust can succeed as a language if it massively differs,
>> visually, from the language it intends to offset (C++).
>>
>
> I don't think Rust can succeed as a language if it massively resembles
>
> the language it intends to offset (C++).
>
> Denis
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


[rust-dev] Type system thoughts

2013-11-15 Thread Gábor Lehel
Hello list,

I have some ideas about typey things and I'm going to write them down. It
will be long.




It would be nice if `Trait1 + Trait2` were itself a trait, legal in the
same positions as any trait. This is already partly true: in trait bounds
on type parameters and super-traits of traits. Where it's not true is trait
objects, e.g. `~(ToStr + Send)`. Having this could remove the need for the
current `~Trait:OtherTraits` special syntax.

I wonder whether lifetimes could also be interpreted as traits, with the
meaning: "[object of type implementing lifetime-trait] does not outlive
[the given lifetime]". This is an honest wondering: I'm not sure if it
makes sense. If it does make sense, it would fit in perfectly with the fact
that 'static is already a trait. Together with the above, it might also
allow a solution for capturing borrowed data in a trait object: you could
write `~(Trait + 'a)`.




The next few are going to be about higher- (or just different-) kinded
generics. To avoid confusion, I'm going to use "built-in trait" to mean
things like `Freeze` and `Send`, and "kind" to mean what it does everywhere
else in the non-Rustic world.

I think the best available syntax for annotating the kinds of types would
be to borrow the same or similar syntax as used to declare them, with
either `type` or `struct` being the kind of "normal" non-generic types (the
only kind of type that current Rust lets you abstract over). [I prefer
`type`, because both structs and enums inhabit the same kind.] This is kind
of like how C++ does it. For example, the kind of `Result` would be
`type`. Our `type` corresponds to C++'s `typename` and
Haskell's `*`, and `type` to C++'s `template class` and Haskell's `* -> * -> *`. So, for example, you could
write the fully kind-annotated signature of an identity function restricted
to Result-shaped types (yeah, actually doing this would be pointless!) as:

fn dumb_id R, type A, type B>(x: R) -> R;

To explicitly annotate the kind of the Self-type of a trait, we could
borrow the `for` syntax used in `impl`s. Here's the fully kind-annotated
version of the `Functor` trait familiar from Haskell:

trait Functor for type Self {
fn fmap(a: &Self, f: |&A| -> B) -> Self;
}

(Obviously, explicitly annotating every kind would be tiresome, and `Self`
is a little redundant when nothing else could go there. I could imagine
`trait Functor for type`, `trait Functor for Self`, and/or `trait
Functor` all being legal formulations of the above. I'll get back to this
later.)

One question with regards to higher-kinded traits is what to do about
methods and `self`. What syntax to use? Does it even make sense? For
syntax, I see two possibilities (snipping from the `fmap` signature):
 - `self: &Self`
 - `&self`
I don't have a preference, but I'll be using the latter for now. And it
*does* make sense. Every trait over a `Self` with parameters of given kinds
gives rise to trait objects with parameters of the same kinds. Basically,
it's always the outermost type constructor that gets erased. In the trivial
case, the `Self` of `ToStr` does not have any parameters, and neither does
`~ToStr`. To see a less trivial case, let's write a variant of Haskell's
`Foldable`:

trait Foldable for type Self {
fn fold(&self, init: &T, folder: |&T, &T| -> T) -> T;
}

Then `&Foldable` is a trait object hiding any foldable container of
ints (for example `~[int]`). There should probably be a way to distinguish
the arguments of the trait from the arguments of Self: so perhaps that
should be something like `&Foldable` instead (but that doesn't
cleanly extend to more than one argument). An example of its use:

fn sum(vals: &Foldable) -> int {
vals.fold(&0, |a, b| a + b)
}
let n = sum(&[1, 2, 3] as &Foldable);

So far we've seen two sorts of kinds: the base kind `type`, and generic
kinds `type<...>`. Another very important one is lifetimes. And I can
imagine two more beyond these:

Like C++, types could be parameterized over constants. Again, the syntax
could mirror their declarations. For example, a function to construct a
fixed-length array:

fn make_n(n: int) -> ~[int, ..N] { [n, ..N] }

Interesting questions here include what types to allow, how or whether to
handle literals of various types as type arguments, and the constant
expression sublanguage.

The other is the potential to abstract over traits. GHC does this with the
`ConstraintKinds` extension. (According to people who have implemented
Haskell compilers, it's actually easier to implement it than not to![1]
Which is pretty crazy.) In that case, just to stick to trivial, useless
examples, and continue with parameter-syntax-follows-declaration, you could
write a generic alias for `Trait1 + Trait2` as:

trait And: A + B { }
impl And for T { }

[1]:
http://www.reddit.com/r/haskell/comments/10w3cx/ideas_for_a_library_that_uses_constraintkinds_to/c6h6di4

(I don't want to dive any more deeply into the motivati

Re: [rust-dev] Type system thoughts

2013-11-16 Thread Gábor Lehel
On Sat, Nov 16, 2013 at 1:07 AM, Huon Wilson  wrote:

> On 16/11/13 03:05, Gábor Lehel wrote:
>
>> It would be nice if `Trait1 + Trait2` were itself a trait, legal in the
>> same positions as any trait. This is already partly true: in trait bounds
>> on type parameters and super-traits of traits. Where it's not true is trait
>> objects, e.g. `~(ToStr + Send)`. Having this could remove the need for the
>> current `~Trait:OtherTraits` special syntax.
>>
>> I wonder whether lifetimes could also be interpreted as traits, with the
>> meaning: "[object of type implementing lifetime-trait] does not outlive
>> [the given lifetime]". This is an honest wondering: I'm not sure if it
>> makes sense. If it does make sense, it would fit in perfectly with the fact
>> that 'static is already a trait. Together with the above, it might also
>> allow a solution for capturing borrowed data in a trait object: you could
>> write `~(Trait + 'a)`.
>>
>>
> How does this interact with vtables?
>

Good question. Here's how I think it might work: `+` would be effectively,
or actually, a type constructor. (Might be easier to think about if you
imagine desugaring to syntax like `Both` instead of `A + B`). As
elsewhere, a type would be uniquely determined by the combination of its
type constructor and its type arguments, so `Foo + Bar` in two separate
places in the program would be the same type, with the same vtable.

Another question is whether `Bar + Foo` would also be the same type.
Looking at what GHC does, it seems `(Show Int, Read Int) ~ (Read Int, Show
Int)` does *not* hold, so they are different types, but when it comes to
constraint-solving time, if you know one you can deduce the other, so
unless you are specifically testing for type equality of constraints then
they're equivalent in practice, which seems reasonable.


>
> Note we can already get something similar with
>
>trait Combine1And2: Trait1 + Trait2 {}
>impl Combine1And2 for T {}
>
>// use ~Combine1And2
>
> which makes it clear how the vtables work, since Combine1And2 has its own
> vtable explicitly constructed from the two traits.


Yeah I know. It feels a bit like using defunctionalization by hand to
simulate HOFs though. Much nicer if things are first class and there are
simple rules, like: "if T1 and T2 are traits, then T1 + T2 is a trait".
Might even simplify some things in the implementation, e.g. traits would
now only have *one* supertrait, there would now only be *one* trait bound
on each type variable, though I'm not too familiar with it.


> (I guess ~(Trait1 + Trait2) would be most useful if one could cast down to
> ~Trait1 and ~Trait2.)
>
>
>
>>
>>
>> The next few are going to be about higher- (or just different-) kinded
>> generics. To avoid confusion, I'm going to use "built-in trait" to mean
>> things like `Freeze` and `Send`, and "kind" to mean what it does everywhere
>> else in the non-Rustic world.
>>
>> I think the best available syntax for annotating the kinds of types would
>> be to borrow the same or similar syntax as used to declare them, with
>> either `type` or `struct` being the kind of "normal" non-generic types (the
>> only kind of type that current Rust lets you abstract over). [I prefer
>> `type`, because both structs and enums inhabit the same kind.] This is kind
>> of like how C++ does it. For example, the kind of `Result` would be
>> `type`. Our `type` corresponds to C++'s `typename` and
>> Haskell's `*`, and `type` to C++'s `template> typename> class` and Haskell's `* -> * -> *`. So, for example, you could
>> write the fully kind-annotated signature of an identity function restricted
>> to Result-shaped types (yeah, actually doing this would be pointless!) as:
>>
>> fn dumb_id R, type A, type B>(x: R) -> R;
>>
>> To explicitly annotate the kind of the Self-type of a trait, we could
>> borrow the `for` syntax used in `impl`s. Here's the fully kind-annotated
>> version of the `Functor` trait familiar from Haskell:
>>
>> trait Functor for type Self {
>> fn fmap(a: &Self, f: |&A| -> B) -> Self;
>> }
>>
>> (Obviously, explicitly annotating every kind would be tiresome, and
>> `Self` is a little redundant when nothing else could go there. I could
>> imagine `trait Functor for type`, `trait Functor for Self`, and/or
>> `trait Functor` all being legal formulations of the above. I'll get back to
>> this later.)
>>
>>
>>
> Could this be:
>
>fn dumb_id, A, B>(x: R) -> R;
>

That seem

Re: [rust-dev] Type system thoughts

2013-11-16 Thread Gábor Lehel
On Sat, Nov 16, 2013 at 12:30 PM, Gábor Lehel  wrote:

> What I was wondering in the parentheses above is whether you could have
> something `type MT>`. In other words whether "templates
> can return templates", which you can't do in C++ but can in Haskell. This
> is closely tied to the question about currying and partial application at
> the type level. If we do have that, then the above is completely equivalent
> to `type MT, type>` (just like `(* -> *) -> * -> *` and `(* ->
> *) -> (* -> *)` are the same thing in Haskell), though it might be nicer to
> write it one way or the other in different cases.
>

I just noticed that if this is true, then it actually unifies your proposal
for syntax with my earlier one. What happens is that `type F` is a
type-level unary function, and `type F` is a nullary function
returning a unary function, and we can consider these to be equivalent.
This is like how, thanks to the absence of side effects and such, Haskell
gets to elide the difference between nullary functions and plain values.
Rust's type level shares these kind of properties with Haskell's value
level, so this is kinda appealing. On the other hand, maybe the added
flexibility doesn't actually gain us anything except a taller learning
curve. But in either case, it's really cool to realize that our two
proposals are not just ad hoc stylistic choices, but in fact both have
sensible theoretical interpretations in the same system with a
correspondence between them.

To recap the options:

 1. Everything must be on the left:

type, type> MT

 2. All parameters must be on the right, and only the final return kind on
the left:

type MT, type>

 3. Both are legal and equivalent, along with forms in between the two
extremes:

type MT>


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Type system thoughts

2013-11-19 Thread Gábor Lehel
On Sun, Nov 17, 2013 at 2:08 AM, Niko Matsakis  wrote:

> On Fri, Nov 15, 2013 at 05:05:28PM +0100, Gábor Lehel wrote:
> > I have some ideas about typey things and I'm going to write them down. It
> > will be long.
>
> I haven't read this all yet, I just want to respond to the first few
> paragraphs. :)
>


Thanks!


> > It would be nice if `Trait1 + Trait2` were itself a trait, legal in the
> > same positions as any trait. This is already partly true: in trait bounds
> > on type parameters and super-traits of traits. Where it's not true is
> trait
> > objects, e.g. `~(ToStr + Send)`. Having this could remove the need for
> the
> > current `~Trait:OtherTraits` special syntax.
>
> It's plausible to imagine, though I wouldn't phrase it that "Trait1+Trait2"
> is a trait, but rather a form of generalized object types. Today object
> types have the form `~Trait` and these generalized object types would
> have the form `~(Trait1+...+TraitN)`. Probably their size would be
> N+1 words to account for the extra vtables, which would make it trival
> to support the (natual) subtyping relation of the form
>
> ~(Trait1+Trait2) <: ~Trait1
> ~(Trait1+Trait2) <: ~Trait2
>
> You can sort of do something similar today with
>
> trait Trait3 : Trait1 + Trait 2 { }
> impl Trait3 for T { }
>

Interesting. Why would (or should) these two have different
representations? Couldn't we think of `+` as a type constructor, that takes
two traits as arguments and makes a trait?

In the same way that

type MyThing = (int, f64);

struct MyThing { a: (int, f64) }

will both have the same representation, couldn't we have `~(Trait1+Trait2)`
be represented just the same as `~Trait3` from the example above, so in a
sense `Trait3` is nothing more than a newtype of `Trait1+Trait2`?

(Couldn't/wouldn't/shouldn't `~Trait3` also be a subtype of `~Trait1` and
`~Trait2`?)

Note that my intuitions here are informed by having done much of the same
things in Haskell with `ConstraintKinds`. For instance, here is Rust's `+`,
but called `:&:`:

http://hackage.haskell.org/package/exists-0.2/docs/Control-Constraint-Combine.html

(And `Exists` in the same package is quite close to Rust's object types.)

But not everything translates directly, because for instance, Haskell
doesn't have subtyping. (Or at least, not in this way -- polymorphic types
with (sub)constraints are in effect a different form of it.)



>
> Note that the current syntax `~Trait:Bounds` is intentionally limited
> in that the traits that can appear in `Bounds` do not offer methods
> but rather give information about the kind of data that the object
> receiver consists of.
>

I know. What I'm trying to explore is whether generalizing the meaning of
`+` might not (alongside being useful generally) remove the need for this
special casing. (My wondering about lifetimes is in a similar vein.)


>
> > I wonder whether lifetimes could also be interpreted as traits, with the
> > meaning: "[object of type implementing lifetime-trait] does not outlive
> > [the given lifetime]". This is an honest wondering: I'm not sure if it
> > makes sense. If it does make sense, it would fit in perfectly with the
> fact
> > that 'static is already a trait. Together with the above, it might also
> > allow a solution for capturing borrowed data in a trait object: you could
> > write `~(Trait + 'a)`.
>
> I don't think of `'static` as a trait, though it is currently
> implemented that way for historical reasons that will hopefully soon
> be eased. Rather, type parameters can have a *lifetime bound*, which
> indicates the minimum lifetime that they can exist for. `'static` is
> just a special case of this.  This might be (for all intents and
> purposes) what you meant.


Which is a worthwhile question. :-)

I currently have two overlapping interpretations of lifetimes:

 1. The intuitive version, where lifetime parameters on borrowed pointers
and structs have the meaning "this object does not outlive the given
lifetime";

 2. By relating them to the universally quantified type variable of
Haskell's `ST` monad, which seems very close to if not precisely the same
thing: in both cases, objects whose type is tagged with a given
(lifetime/type variable) can't "escape" the given computation.

I can make sense of the "lifetime bounds" on type parameters which you
describe in terms of the first, because it makes sense intuitively, but not
the second. How does a universally quantified type variable end up behaving
in any way, shape, or form like a type class?

In particular, one should be able to do
> `~Trait:'a`, which is interpreted as

Re: [rust-dev] Please simplify the syntax for Great Justice

2013-11-19 Thread Gábor Lehel
In case this helps, I recently noticed that the sigils correspond to
possessive pronouns:

'~' => "my",
'&' => "their",
'@' => "our"

Of course, `@` might be going away, but `Rc<>`, `Gc<>`, and so forth all
(will) have the same intuitive content, only different representations
(among other properties). Similarly `~Foo` and plain `Foo` both mean "my",
while having differences in other areas, in which case yeah, telling you
that `Foo` is stored in-place while `~Foo` is stored on the heap doesn't
help you if you don't know what heap allocation is about. But maybe this is
something.


On Tue, Nov 19, 2013 at 10:12 AM, Jordi Boggiano  wrote:

> On Tue, Nov 19, 2013 at 5:17 AM, Patrick Walton 
> wrote:
> > I've observed a lot of beginning Rust programmers treat the language as
> "add
> > sigils until it works". (I have specific examples but don't want to name
> > people here; however, feel free to contact me privately if you're
> curious.)
>
> I feel like I have to out myself as one of the idiot newcomers that do
> this, just for the sake of the discussion. I have no systems language
> background and frankly never had to consider the difference between
> the heap or the stack in the past 10-some years of writing code.
>
> I don't really think having new vs ~ would help me avoid this problem.
> The issue I believe is that the language looks understandable enough
> for the average joe used to higher level web languages (php, python,
> ruby). That's a great thing of course, that I can just jump in and
> mostly grasp what's going on, while past attempts at C++ tinkering
> quickly ended in tears. It also means that you have lots of people
> that come in and are capable of getting stuff to compile but won't
> necessarily understand the small print. Often there is an alternative
> to pooping sigils all over the code, but if you don't understand the
> concepts behind it it's hard to reason about what those alternatives
> could be.
>
> I think I'm getting better with this over time, and the rust libraries
> also get more usable and consistent leading to less ~ insanity, but
> one thing that would have helped early on is a good warning in the
> docs about this, and a good explanation of what the hell is going on
> (not one geared towards C++ devs using lingo that only low level devs
> are familiar with).
>
> I realize it's no easy task, and that arguably I should probably just
> read a book, but at the same time it's an amazing feat I think that
> the language is so accessible while remaining at such a low level, so
> if we manage to improve the onboarding process it would probably be
> very beneficial. There are tons of web devs that are interested in
> doing things faster/lower level - if only for fun. Maybe it's worth
> having a chapter for them in the docs. I'd happily help review that
> and point out unclear things :)
>
> Cheers
>
> --
> Jordi Boggiano
> @seldaek - http://nelm.io/jordi
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] list of all reserved keywords of the language

2013-11-20 Thread Gábor Lehel
On Wed, Nov 20, 2013 at 7:37 PM, Brian Anderson wrote:

> On 11/20/2013 09:14 AM, Niko Matsakis wrote:
>
>> On Tue, Nov 19, 2013 at 07:19:15AM -0800, Patrick Walton wrote:
>>
>>> The liveness analysis uses the infinite nature of `loop`, and it was
>>> felt that special-casing the `true` boolean like Java does is a hack.
>>>
>> I personally like `loop`. It reads nicely. It happens quite frequently
>> that I have a loop that works out best if I test the condition in the
>> middle, rather than the front or the end, or where the condition is
>> something quite involved; in such situations, `loop { ... }` is
>> perfect, and `while true { ... }` always feels second-class. I
>> recognize this is silly of me, though. :)
>>
>>
>>
> I also adore `loop` and rarely use `while`. I write code like this
> constantly:
>
> loop {
> match port.recv() {
> case DoSomething => { }
> case Exit => break
>
> }
> }
>

This is an even sillier idea, but then what about keeping `loop` and
dropping `while`?




>  ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] list of all reserved keywords of the language

2013-11-20 Thread Gábor Lehel
Hmm? I think you think I meant that `loop` should accept a condition in
front of the loop, like `while`? In fact my idea was sillier: just replace
all instances of `while foo { bar }` with

loop {
if !foo { break }
bar
}


On Wed, Nov 20, 2013 at 9:56 PM, Benjamin Striegel
wrote:

> > This is an even sillier idea, but then what about keeping `loop` and
> dropping `while`?
>
> I'm not sure this is possible to disambiguate in the grammar. You'd have
> to be able to tell the difference between the infinite form:
>
> loop 
>
> ...and the conditional form:
>
> loop  
>
> ...while keeping in mind that  can also be a .
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
>


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Placement new and the loss of `new` to keywords

2013-11-30 Thread Gábor Lehel
On Sat, Nov 30, 2013 at 10:17 PM, Patrick Walton wrote:

> On 11/30/13 1:01 AM, Kevin Ballard wrote:
>
>> As
>> for placement new, while it needs to be supported in some fashion, it's
>> going to be used pretty rarely, such that I don't think it's worth
>> reserving a keyword just for that.
>>
>
> Placement new is not going to be used rarely. In Servo, for example, it's
> used all over the place.


Can that (or even the totality of currently existing Rust code!) be
considered a representative sample? I'm guessing, though I don't have
knowledge, that web browser engines written in C++ also use placement new a
lot. But I'd also think that if you consider all C++ code in existence, you
would find that it's used pretty rarely.

(Of course, C++ /does/ reserve the keyword. But presumably not for the
reason that placement new is super-common.)


>
>
>  We've been moving stuff from the language into the libraries, yes. But
>> losing ~ seems like losing a big part of the "flavor" of Rust, so to
>> speak.
>>
>
> That "flavor" is much of the reason for Rust's reputation as an overly
> complex language.
>

I really like the way current Rust thinks of the various sigils as "just
another constructor". I also like the consistency with other major
languages of `new`, so I'm conflicted. And yes, the constructor-like
property seems unfortunately difficult to extend to third-party smart
pointers... but in general I really don't like the solution to "$thing is
not as convenient with third-party types as with the built-in ones" being
to make it equally inconvenient for the built-in ones. Now you have two
problems.

I think the complexity you mention is an inherent part of the semantics of
Rust. The most intuitive and ergonomic way to expose it to the programmer
is a very important consideration, but you're still not doing more than
pushing the complexity around.

Basically, I'm not sure Rust's reputation as a complex language is
something you could cause to go away in this fashion. If you replace all of
the sigils with explicit `Foo` and `new` syntax, will that make people
see it as less complex?


>
> Patrick
>
> ___
>
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Placement new and the loss of `new` to keywords

2013-11-30 Thread Gábor Lehel
On Sat, Nov 30, 2013 at 10:26 PM, Patrick Walton wrote:

> On 11/30/13 1:17 PM, Patrick Walton wrote:
>
>> Especially since the replacement
>>> convention, as seen in PR #10697, is pretty bad (namely, using ::init()
>>> instead of ::new(); init() to me seems as though it should merely
>>> initialize a value, not construct a new value. Heck, the old Path
>>> convention of using Path(..) is better than Path::init(..)).
>>>
>>
> Honestly, I'm not in love with `init` either. I don't find it particularly
> intuitive, and alternatives are welcome. But I do think that using a sigil
> for allocation and the lack of placement new are problems that needs fixing.


Pretty basic question, but: what does placement new in a Rust context even
mean, precisely? The very same thing as in C++?

Because in C++ allocation and placement new are pretty orthogonal. If you
write `new Foo`, it means: allocate memory for a Foo and run its
constructor in that memory. If you write `new (foo) Foo`, it means: *don't*
allocate memory, just construct a Foo directly into `foo`, which is some
existing piece of memory I provide. So while `new` is the allocation
operator, placement new is in fact just a funny syntax for running
constructors. Which I think is confusing as heck.

All of that leading up to: if these are different things, mightn't it make
sense for Rust to use different syntax for them?

(And if placement new in Rust is different from placement new in C++, then
what's the difference?)



>
>
> Patrick
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Placement new and the loss of `new` to keywords

2013-11-30 Thread Gábor Lehel
On Sat, Nov 30, 2013 at 11:35 PM, Benjamin Striegel
wrote:

> > The other suggestion that's been floated that satisfies all of these
> constraints is `alloc`, and honestly, if `new` is that unpopular maybe we
> should just switch to that.
>
> I much prefer `new` to `alloc`. It *is* a shame that `foo::new()` has to
> change, but `alloc` is much grosser than `init`. And maybe we can think of
> a better naming convention for constructors... perhaps `foo::make()`.
>

FWIW, in Objective-C:

[MyType new] is shorthand for [[MyType alloc] init]

(Not saying we should do the same, just some prior art.)

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


[rust-dev] Distilled essence of placement new

2013-12-03 Thread Gábor Lehel
Prompted by the recent discussion about the syntax to use for placement
new, I took a few steps back to think about the bigger picture. I'm glad I
did, because I now think that the important part of the feature has been
misidentified so far, and correctly identifying it opens the door to a much
more general and powerful mechanism, with a smaller conceptual "surface
area", and whose implementation burden doesn't appear any larger than that
of the placement new feature as up-to-now envisioned (in particular, the
magic sauce is the same in both).


## What is placement new?

The C++ syntax for "placement new" is: `new (place) MyType(args...)`, where
place is a memory address. This constructs a new instance of `MyType` into
`place`. This is useful because it lets you avoid the overhead of first
constructing the `MyType` elsewhere and then copying or moving it into
`place`.

I used to think that placement new is not commonly used in C++, but I have
revised my opinion. As Daniel Micay reminded me of in the other thread, C++
now has things like `emplace_back` in many places which use placement new
under the hood to avoid the cost of a move. The reason C++ didn't
*previously* use placement new very often is that the lack of perfect
forwarding and variadic templates made doing so in a generic way way too
painful.

This is also one of the things that got me thinking: restricting our
version of placement new to smart pointers seems artificial and not very
well-motivated, except if by the fact that smart pointers are an important
use case and the built-in ones already have the feature. But we also want
`emplace_back`, don't we?


## What is placement new, *really*?

The C++ signature of `emplace_back` is:

template // I'm ignoring the allocator
template
void vector::emplace_back(Args&&... args);

What it does is reserve some memory at the end of the vector, then use
placement new to invoke the constructor of `T`, with `args`, into that
memory. How could we translate this to Rust? First of all, Rust doesn't
have constructors as a distinguished language feature. But a constructor is
not anything more than a function statically known based on a type: in
Rust, a (self-less) trait method. Rust doesn't have variadic templates
either, but we could use tuples. (As for perfect forwarding, I suspect it
solves a problem Rust doesn't have.) So then we have:

trait Construct {
fn construct(Args) -> Self;
}

fn emplace_back>(self: &mut Vec, args:
Args);

So in reality, placement new is nothing more than a statically dispatched
trait method call. The only reason C++ needs special syntax for it is
because C++ distinguishes constructors from other functions. In particular,
it's worth noticing that if C++'s functions use a hidden out-pointer
argument like Rust's functions do, then the C++ version of `emplace_back`
could just as well take a function pointer returning `T` as a non-type
template parameter, and call that instead of T's constructor. (Except C++
doesn't let you make a function pointer to a constructor either, so you
would still want both versions. C++'s complications are of its own design.)

(It's also worth noticing that C++ is using the same
pass-the-environment-explicitly encoding of closures here that Rust
occasionally does.)


## Magic sauce

All of that begs a question: if placement new is not much more than calling
a trait method, then why are we in the process of adding placement new? If
what we're adding isn't placement new, then what *is* it? Well: we *could*
use something like the above Rust version of `emplace_back`, but it would
be super-tedious. Right now if you write `~(this thing)` or `@(that
thing)`, this thing and that thing are constructed (placement newed)
directly into the allocated memory -- where this thing and that thing can
be any expression. That's really nice. We want other smart pointers to be
able to do that.

How does it work? If this thing or that thing is a function call, then the
out-pointer is set to the allocated memory. If it's an expression, then the
compiler emits code to put the result of the expression directly into the
memory. Without having direct knowledge, I imagine that's the same way it's
planned to work for other smart pointers. (I can't imagine any other way to
do it.) What this implies is that the function that allocates the memory is
monomorphized (has a separate version generated) not only on the type of
the object being constructed, but also on the specific expression
constructing it. This is no different from what happens with the current
built-in smart pointers, where the compiler has built-in special-cased code
generation for them. It's also no different from C++'s `emplace_back`,
except there the possible "expressions" are limited to the constructors
available for the type. (If you consider the hypothetical version passing a
function pointer as a template argument, then the number of possibilities
is again much larger.)

So what we're add

Re: [rust-dev] Distilled essence of placement new

2013-12-03 Thread Gábor Lehel
Yes, that was one of the things that passed through my mind as well. One
difference is that those are passed at runtime, whereas here everything
happens at compile time. I remember LLVM had difficulty reliably optimizing
runtime closure passing code, like with the old iterators.


On Wed, Dec 4, 2013 at 5:05 AM, Daniel Micay  wrote:

> This looks a lot like what stack once functions would provide.
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Distilled essence of placement new

2013-12-04 Thread Gábor Lehel
What's the current plan for the implementation of placement new as you
envision it? In particular what would the trait used by Gc etc. to hook
into it look like? How does it do the thing where expressions are
constructed directly into place? I agree with many parts of your analysis
(different argument evaluation orders implicitly would indeed be
unfortunate), but I want to have a clearer picture of the other alternative
before discussing it further.


On Wed, Dec 4, 2013 at 5:57 AM, Patrick Walton  wrote:

> I should elaborate: I think you've successfully formulated placement new
> in terms of unboxed closures as monomorphized traits whose "call" method
> takes self by value. That is, something like (in Rust of the future):
>
> fn GC>(f: F) -> GC
>
> The difference, though, is that the construction of the closure is
> implicit at the call site in this proposal. This is what I'm uncomfortable
> with. Rust is a strict language, not a lazy one, and the order of
> evaluation of function arguments is one of the things that one has
> heretofore been unable to change. I would prefer that a different argument
> evaluation order would necessitate some sort of annotation at the call site
> (e.g. ocaml's "lazy"). But once you've done that you've essentially
> recreated something equivalent to placement new, but more verbose since you
> have to name a method when you allocate into an object (e.g. instead of
> "box(my_arena) foo" you'd have to write "my_arena.alloc(lazy foo)". I think
> we would end up wanting the sugar.
>
> Patrick
>
> "Gábor Lehel"  wrote:
>>
>> Yes, that was one of the things that passed through my mind as well. One
>> difference is that those are passed at runtime, whereas here everything
>> happens at compile time. I remember LLVM had difficulty reliably optimizing
>> runtime closure passing code, like with the old iterators.
>>
>>
>> On Wed, Dec 4, 2013 at 5:05 AM, Daniel Micay wrote:
>>
>>> This looks a lot like what stack once functions would provide.
>>>
>>
>>
>>
>> --
>> Your ship was destroyed in a monadic eruption.
>>
>> --
>>
>> Rust-dev mailing list
>> Rust-dev@mozilla.org
>> https://mail.mozilla.org/listinfo/rust-dev
>>
>>
> --
> Sent from my Android phone with K-9 Mail. Please excuse my brevity.
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Type system thoughts

2013-12-04 Thread Gábor Lehel
On Mon, Dec 2, 2013 at 7:18 PM, Niko Matsakis  wrote:

>
> OK, I read a bit more. I've been working on a blog post about "HKR"
> (higher-kinded Rust) and you've been elaborating on some of the same
> themes as I was thinking about (naturally enough). All makes sense.
>

...which was part of the impetus for finally getting my thoughts down on
"paper". :)


>  In the absence of an erased keyword, I think that higher-kinded traits
> and objects are simply incompatible. The situation is not as simple as
> you made it out, as the trait may itself have generic parameters of
> higher-kind.


Without having thought about very deeply about whether it's true (can you
provide an example?), wouldn't this be more accurately phrased as:
"(higher-kinded?) traits with higher-kinded generic parameters and objects
are simply incompatible"? In other words, if the problem is with the
higher-kinded generic parameters, then why not forbid objects only in that
case, and support them in their absence?


> > The other is the potential to abstract over traits. GHC does this with
> the
> > `ConstraintKinds` extension. (According to people who have implemented
> > Haskell compilers, it's actually easier to implement it than not to![1]
> > Which is pretty crazy.)
>
> I'm not sure this would be true for Rust, but perhaps.
>

I have no idea either - it's astonishing enough that it's true for Haskell.
:)


>
> > There's two further issues that higher-kinded generics raise...
>
> This is as far as I got in this round. More later. :)
>

OK. :)

(I also still have yet to find serious time for DST parts [2..4]...)


>
>
> Niko
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Interesting talk about (scala) language/compiler complexity

2013-12-05 Thread Gábor Lehel
In a similar vein, see this list of complaints against Scala by Edward
Kmett, possibly the most brilliant and prolific Haskell programmer on Earth:

http://www.reddit.com/r/haskell/comments/1pjjy5/odersky_the_trouble_with_types_strange_loop_2013/cd3bgcu

I'll try to comment with my understanding of where Rust stands on each
point:


>-
>
>If you take any two of the random extensions that have been thrown
>into scala and try to use them together, they typically don't play nice.
>e.g. Implicits and subtyping don't play nice together.
>
>
We have to stay careful, but I think the various parts of Rust's type
system fit together amazingly well right now.



>-
>
>Type inference works right up until you write anything that needs it.
>If you go to write any sort of tricky recursive function, you know, where
>inference would be useful, then it stops working.
>
> Not completely sure about this, but hopeful. Can anyone else make a
better-informed comment?



>
>-
>
>Due to type erasure, its easy to refine a type in a case expression /
>pattern match to get something that is a lie.
>
>
I don't think Rust has this problem.



>
>-
>
>Free theorems aren't.
>
>
This is referring to the idea that if type variables are completely opaque
to a generic function parameterized over them, then you can formulate some
equational theorems for the function "for free", which you know will always
be true, because they can't not be. For example, for the Haskell function
fmap :: forall a b. (a -> b) -> (f a -> fb):

fmap f . fmap g = fmap (f . g)
fmap id = id

Due to unrestricted (non-mutation) side effects Rust flunks this, but it
would be nice if, for functions which can be assumed to be pure, it didn't.
For that we have to be vigilant about never allowing things like
dynamic_cast or various built-in intrinsics on completely generic types, at
least outside of unsafe code. This seems more hopeful, but Rust still
flunks it right now: because of, for one instance, size_of::(). I'm not
sure whether there are other instances. It would be nice if this could be
addressed.



>-
>
>Since you can pass any dictionary anywhere to any implicit you can't
>rely on the canonicity of anything. If you make a Map or Set using an
>ordering, you can't be sure you'll get the same ordering back when you come
>to do a lookup later. This means you can't safely do hedge unions/merges in
>their containers. It also means that much of scalaz is lying to itself and
>hoping you'll pass back the same dictionary every time.
>
> Rust smartly avoids this problem.



>
>-
>
>The container types they do have have weird ad hoc overloadings. e.g.
>Map is treated as an iterable container of pairs, but this means you can't
>write code that is parametric in the Traversable container type that can do
>anything sensible. It is one of those solutions that seems like it might be
>a nice idea unless you've had experience programming with more principled
>classes like Foldable/Traversable.
>
> Not quite sure what sort of overloadings this is referring to, but if/when
we add an Iterable trait, this means that perhaps the impl for maps should
iterate only over values, rather than key-value pairs, so that it makes
more sense to use the same generic code for both e.g. maps and arrays. (Of
course a (key, value) iterator should still be provided separately.)

When we gain higher-kinded types, we should also be able to add the actual
Foldable and Traversable traits themselves.



>
>-
>
>You wind up with code that looks like myMap.map(...).toMap all over
>the place due to CanBuildFrom inference woes.
>
> No idea what this is about.



>
>-
>
>Monads have to pay for an extra map at the end of any comprehension,
>because of the way the for { } sugar works.
>
> We have neither monads nor sugar at the moment, but if we ever do (not
quite as crazy as it sounds, given that C# also has a version of it in
LINQ), we should presumably try to remember this problem and avoid it.



>
>-
>
>You have type lambdas. Yay, right? But now you can't just talk about 
> Functor
>(StateT s IO). Its Functor[({type F[X] = StateT[S,IO,X]})#F], and you
>have to hand plumb it to something like return, because it basically
>can't infer any of that, once you start dealing with transformers ever. The
>instance isn't directly in scope. 12.pure[({type F[X] =
>StateT[S,IO,X]})#F] isn't terribly concise. It can't figure out it
>should use the inference rule to define the implicit for StateT[S,M,_]from 
> the one for
>M[_] because of the increased flexibility that nobody uses.
>
>
Rust doesn't have type lambdas and almost certainly never will.



>
>-
>
>In this mindset and in the same vein as the CanBuildFrom issue, things
>like Either don't have the biased flatMap you'd expect, somehow
>encouraging you to use other tools, just in c

Re: [rust-dev] Interesting talk about (scala) language/compiler complexity

2013-12-05 Thread Gábor Lehel
On Thu, Dec 5, 2013 at 5:07 PM, Daniel Micay  wrote:

> On Thu, Dec 5, 2013 at 10:43 AM, Gábor Lehel  wrote:
>
>> The container types they do have have weird ad hoc overloadings. e.g. Map
> >> is treated as an iterable container of pairs, but this means you can't
> write
> >> code that is parametric in the Traversable container type that can do
> >> anything sensible. It is one of those solutions that seems like it
> might be
> >> a nice idea unless you've had experience programming with more
> principled
> >> classes like Foldable/Traversable.
> >
> > Not quite sure what sort of overloadings this is referring to, but
> if/when
> > we add an Iterable trait, this means that perhaps the impl for maps
> should
> > iterate only over values, rather than key-value pairs, so that it makes
> more
> > sense to use the same generic code for both e.g. maps and arrays. (Of
> course
> > a (key, value) iterator should still be provided separately.)
> >
> > When we gain higher-kinded types, we should also be able to add the
> actual
> > Foldable and Traversable traits themselves.
>
> I think it makes sense to treat a map as a sequence of tuples.


When iterating over a map, sure thing. When writing generic code over any
kind of container, not so sure. In that case a `Map` can be viewed as
V values indexed over K keys, while `[T]` can be viewed as T values indexed
over `uint` keys -- and so perhaps there should be a separate
`IterableWithKey` trait for this kind of thing, where arrays would also be
iterated as (index, value) pairs.

That said, maybe you wouldn't ever want to write generic code over
`Iterable`, only `Iterator`, and so this concern wouldn't actually end up
applying.



>
> >> You wind up with code that looks like myMap.map(...).toMap all over the
> >> place due to CanBuildFrom inference woes.
> >
> > No idea what this is about.
>
> I think this is similar to `collect` in Rust, where a container type
> needs to be provided and usually can't be inferred. I'm not sure how
> it could be done differently.
>

Hmm. Seems like the sort of thing that HKT might help with.



>
> >> Monads are toys due to the aforementioned restriction. (>>=) is called
> >> flatMap. Any chain of monadic binds is going to be a series of non-self
> >> tailcalls. A function calls flatMap which calls a function, which calls
> >> flatMap... This means that non-trivial operations in even the identity
> >> monad, like using a Haskell style traverse for a monad over an arbitrary
> >> container blows the stack after a few thousand entries.
> >
> > Rust is not any better on this front. While we still had segmented
> stacks we
> > could've handled this, if not in constant space but at least on the
> stack,
> > but we no longer do.
> >
> > I'm wondering whether the fact that we're going to have moves and drops
> > statically tracked by the compiler might make it more realistic to think
> > about TCE again. I think the biggest remaining obstacle would be the
> calling
> > convention.
>
> I don't think it's a good idea to add any frontend support for
> tail-call elimination. It's not something that's ever going to be
> acceptable with optimizations disabled because it drastically changes
> debugging.
>

That seems like something the programmer would be in a good position to
make an informed choice about. The other obstacles seem more significant
than this one.

(I'm assuming that *if* we ever add TCE, it would be have to be invoked
explicitly (there's currently the `be` keyword reserved for this, which
seems a bit strange, but not as if I have a better idea). So it's not like
the compiler would always unconditionally eliminate tail calls and the
unsuspecting programmer would be foiled in her debugging. She would be able
to pick her poison.)


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Higher-Kinded Types vs C++ "Combos"

2013-12-07 Thread Gábor Lehel
Short version: yes, higher-kinded types and template template parameters
are the same thing. (`template class` is just one particular
higher kind; there's also `template class> class` and so
on, and varying the number of parameters, etc., which you probably know.)

Longer version hopefully upcoming if/when I manage to digest the rest of it.


On Sat, Dec 7, 2013 at 8:10 AM, David Piepgrass wrote:

> Rust newb here. I have theoretical questions.
>
> Recently I noticed that Higher-Kinded Types (HKTs) have been mentioned on
> the mailing list a lot, but I had no idea what a HKT was, or what it might
> be good for. After reading about them a little, they reminded me of C++'s
> "template template parameters". In C++ you can almost write something like
> this:
>
> template  class collection>
> struct Numbers {
>collection integers;
>collection floats;
> };
>
> So then you can write Numbers for a structure that contains
> vector collections, and Numbers for a structure that contains
> list collections. EXCEPT that it doesn't actually work, because
> vector has two template parameters (the second one, the allocator, is
> normally left at its default). Let's ignore that, though.
>
> So that brings me to my first question: is this what "higher-kinded types"
> means? What is the difference, if any, between HKT and C++ "template
> templates"?
>
> However, as a C++ developer I never actually used a "template template"
> parameter because I didn't know they existed for a long time. So instead I
> would have written this, which has the same end-result:
>
> struct VectorTrait
> {
> template
> struct collection { typedef vector type; };
> };
> struct ListTrait
> {
> template
> struct collection { typedef list type; };
> };
>
> template
> struct Numbers
> {
> Traits::collection::type integers;
> Traits::collection::type floats;
> };
> // Use Numbers for vector, Numbers for list.
>
> This is clunkier, but it would have been a bit simpler if C++ supported
> templatized typedefs:
>
> struct VectorTrait
> {
> template typedef vector collection;
> };
> struct ListTrait
> {
> template typedef vector collection;
> };
>
> template
> struct Numbers
> {
> Traits::collection integers;
> Traits::collection floats;
> };
> // Now write Numbers instead of Numbers,
> //   Numbers instead of Numbers.
>
> I have found that because of the existence of typedef, "template template"
> parameters are never actually necessary; so far, I've never seen a
> situation where the typedef-based solution wasn't almost as good. Also, I
> have found that "trait" types filled with typedefs seem to be a more
> general thing than "template template"; they allow you to do things that
> would be very difficult or impossible without them. For example you can use
> typedefs-in-a-struct to create circular references among types that don't
> "know" about each other:
>
> // I call this a "Combo"; I don't know if the technique has a standard name
> struct MyCombo {
> typedef ConcreteA A;
> typedef ConcreteB B;
> typedef ConcreteC C;
> };
> template
> class ConcreteA { Combo::B* b; ... };
> template
> class ConcreteB { Combo::C* c; ... };
> template
> class ConcreteC { Combo::A* b; ... };
>
> Here I've created a network of types (ConcreteA,
> ConcreteB, and ConcreteC) that are linked together
> through the "Combo" type MyCombo, so the types can all use each other, but
> none of the types refer to each other directly. This design allows you to
> freely swap in different implementations of A, B, and C; it has similar
> advantages to "dependency injection" or "inversion of control" in languages
> like Java and C#, except that the linkages are all defined statically at
> compile-time, so no dynamic dispatch is required.
>
> Without the ability to define "typedefs", this approach is not possible at
> all if there is a cyclic relationship. Also, if the combo declares more
> than three types, it becomes impractical to specify all those types on the
> classes directly as type parameters.
>
> In C# I learned that this quickly becomes a major problem if you need to
> parameterize on more than one or two types. I tried to do "generic" math
> (which requires at least two type parameters due to the under-designed
> standard libraries) and I also implemented a GiST data structure (see
> http://en.wikipedia.org/wiki/GiST), and found out that the lack of any
> analog to C++ typedef makes both of those tasks very clumsy, while also
> making the code hard to read, because you end up with a rats' nest of type
> parameters (or if you omit (otherwise necessary) type parameters, you might
> use lots of casts instead.)
>
> So I guess that leads me to two more questions.
>
> 2. Does Rust have a "typedef" equivalent that can be used in this way?
> 3. Does it make sense to just suggest "just use typedefs instead of
> Higher-Kinded Types"?
>
>
> ___
> Rust-dev mailing list
> Rust-dev@m

Re: [rust-dev] Higher-Kinded Types vs C++ "Combos"

2013-12-07 Thread Gábor Lehel
In all these cases with typedefs, you're still implicitly relying on
higher-kinded types, along with what are usually referred to as associated
types:

http://smallcultfollowing.com/babysteps/blog/2013/04/02/associated-items/
http://smallcultfollowing.com/babysteps/blog/2013/04/03/associated-items-continued/

Kinds are basically the types of types. In the normal case there's two
varieties:

 - The base kind, which is the kind of what you normally think of as
"types", i.e. things which can be the type of values. In C++ this is called
typename/class (in Haskell: *).

 - The kind of functions from types of one kind to types of another
(potentially the same) kind; in other words, the kind of types
parameterized over type arguments (of varying kind). The simplest case is
the kind of functions from the base kind to the base kind, in C++
template class (in Haskell: * -> *).

Then you can iterate the second rule however you like for an infinite
regress of different kinds.

In all of these examples you're using an implicit protocol (type of a
particular kind with a particular name as a member) to encode associated
types. The associated types themselves are of higher kinds. In the Numbers
example, the type parameter itself is the base kind, but has an associated
type with a templatized (function) kind, which is what you're actually
using. In this case you could just as easily skip the middleman and pass
around the higher-kinded type directly, but associated types also give you
the ability to "branch" over the input type, which higher-kinded types by
themselves do not. (This is what happens, for example, when strings declare
that their element type is a character, instead of being parameterized over
an element type.)

Basically, this form of "typedefs" already implies HKT, and if we were to
add HKT, there would be no reason to restrict them to only being associated
types.


On Sat, Dec 7, 2013 at 9:05 PM, Gábor Lehel  wrote:

> Short version: yes, higher-kinded types and template template parameters
> are the same thing. (`template class` is just one particular
> higher kind; there's also `template class> class` and so
> on, and varying the number of parameters, etc., which you probably know.)
>
> Longer version hopefully upcoming if/when I manage to digest the rest of
> it.
>
>
> On Sat, Dec 7, 2013 at 8:10 AM, David Piepgrass wrote:
>
>> Rust newb here. I have theoretical questions.
>>
>> Recently I noticed that Higher-Kinded Types (HKTs) have been mentioned on
>> the mailing list a lot, but I had no idea what a HKT was, or what it might
>> be good for. After reading about them a little, they reminded me of C++'s
>> "template template parameters". In C++ you can almost write something like
>> this:
>>
>> template  class collection>
>> struct Numbers {
>>collection integers;
>>collection floats;
>> };
>>
>> So then you can write Numbers for a structure that contains
>> vector collections, and Numbers for a structure that contains
>> list collections. EXCEPT that it doesn't actually work, because
>> vector has two template parameters (the second one, the allocator, is
>> normally left at its default). Let's ignore that, though.
>>
>> So that brings me to my first question: is this what "higher-kinded
>> types" means? What is the difference, if any, between HKT and C++ "template
>> templates"?
>>
>> However, as a C++ developer I never actually used a "template template"
>> parameter because I didn't know they existed for a long time. So instead I
>> would have written this, which has the same end-result:
>>
>> struct VectorTrait
>> {
>> template
>> struct collection { typedef vector type; };
>> };
>> struct ListTrait
>> {
>> template
>> struct collection { typedef list type; };
>> };
>>
>> template
>> struct Numbers
>> {
>> Traits::collection::type integers;
>> Traits::collection::type floats;
>> };
>> // Use Numbers for vector, Numbers for list.
>>
>> This is clunkier, but it would have been a bit simpler if C++ supported
>> templatized typedefs:
>>
>> struct VectorTrait
>> {
>> template typedef vector collection;
>> };
>> struct ListTrait
>> {
>> template typedef vector collection;
>> };
>>
>> template
>> struct Numbers
>> {
>> Traits::collection integers;
>> Traits::collection floats;
>> };
>> // Now write Numbers instead of Numbers,
>> //   Numbers instead of Numbers.
>>
>> I have found that beca

Re: [rust-dev] Let’s avoid having both foo() and foo_opt()

2013-12-07 Thread Gábor Lehel
I agree with this proposal. I also strongly agree with spir that we should
distinguish programmer errors from legitimate result possibilities. A
function should only fail directly if, were it to return a `None` or `Err`,
the only reasonable the thing caller could do would be to fail itself.

Somewhat formally, a function has a domain and a codomain, i.o.w. the
possible input and output values which make sense. If the type used for the
domain is "too big", and allows values which don't make sense in the
context of the function, then this opens up the possibility that the
programmer might mess up and nonetheless pass in one of those values. In
this case the right thing is for the function to fail at runtime: there's
not much else it could do. (This is the null argument /
NullPointerException situation in many other languages.)

The converse case is if the codomain is too small, and doesn't provide a
way to represent all of the possible output values. For example, a search
function might not have a way to say "not found". In this case the function
might also want to fail. But in Rust's type system it's much, much, much
easier to make types bigger than it is to make them smaller, so there's no
excuse not to do so. This function should just return an Option.

Of course, it's not always black and white, and what "makes sense" can
depend on the situation. Sometimes if the container we pass to the search
function doesn't contain the value we're searching for, then it's a bug.
The programmer can always signal this by just calling unwrap() (which I
agree should have a better name, if we can find one) or expect(). But I
think it's also reasonable to add convenience functions if this would be
the case often enough to make them worthwhile. But they should be clearly
presented as "added convenience functions, which fail in [these cases]" and
not as the default option.

Haskell has a bunch of partial functions which do their equivalent of
failing in their Prelude, such as `head :: [a] -> a`, and they've been
regretting it ever since. IMHO, let's try to keep our functions total
whenever we possibly can.


On Fri, Dec 6, 2013 at 9:41 PM, Simon Sapin  wrote:

> We have some functions and methods such as [std::str::from_utf8](http://
> static.rust-lang.org/doc/master/std/str/fn.from_utf8.html) that may
> succeed and give a result, or fail when the input is invalid.
>
> 1. Sometimes we assume the input is valid and don’t want to deal with the
> error case. Task failure works nicely.
>
> 2. Sometimes we do want to do something different on invalid input, so
> returning an `Option` works best.
>
> And so we end up with both `from_utf8` and `from_utf8`. This particular
> case is worse because we also have `from_utf8_owned` and
> `from_utf8_owned_opt`, to cover everything.
>
> Multiplying names like this is just not good design. I’d like to reduce
> this pattern.
>
> Getting behavior 1. when you have 2. is easy: just call `.unwrap()` on the
> Option. I think we should rename every `foo_opt()` function or method to
> just `foo`, remove the old `foo()` behavior, and tell people (through
> documentation) to use `foo().unwrap()` if they want it back?
>
> The downsides are that unwrap is more verbose and gives less helpful error
> messages on task failure. But I think it’s worth it.
>
> What do you think?
>
> (PS: I’m guilty of making this worse in #10828, but I’d like to discuss
> this before sending pull requests with invasive API changes.)
>
> --
> Simon Sapin
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Let’s avoid having both foo() and foo_opt()

2013-12-07 Thread Gábor Lehel
C# is actually adding null-chaining syntax sugar as well:
http://adamralph.com/2013/12/06/ndc-diary-day-3/?1

C# is a very sugar-happy language, and I think it's wise for Rust to be
conservative on this front (much easier to add things later than to remove
them), but given how pervasive it is, I do wonder whether it wouldn't make
sense to add sugar for Option, at least eventually. (`int?` at the type
level is really nice, too... too bad it doesn't play so well with Rust's
sigils. Introducing the potential confusion between `~?T` and `?~T` is
probably a step too far.)


On Sat, Dec 7, 2013 at 2:14 AM, Huon Wilson  wrote:

> On 07/12/13 12:08, Jordi Boggiano wrote:
>
>> On Sat, Dec 7, 2013 at 2:01 AM, spir  wrote:
>>
>>> On 12/07/2013 01:12 AM, Gaetan wrote:
>>>
 I am in favor of two version of the api: from_str which has already done
 the unwrap, and a from_str_safe for instance that returns a Result or
 option.

>>> This provides the important semantic information (that I've evoked at the
>>> end end of a long earlier reply in this thread) of whether func failure
>>> is
>>> expected and belongs to the logic of the present app and we must deal
>>> with
>>> it, or not.
>>>
>>> But I'm still shared on this topic for finding it also annoying, like
>>> Simon,
>>> to have to duplicate whole catogories of such funcs (of which we cannot
>>> know
>>> in advance whther they'll fail or not), if only the interface as
>>> apparently
>>> proposed by Gaëtan.
>>>
>> Syntax sugar like this would be nice:
>>
>> let str = std::str::from_utf8("Parse this optimistically, and fail
>> otherwise");
>> // str is a string or the task fails
>>
>> vs.
>>
>> let opt_str = std::str::from_utf?("Parse this if valid"); // note the
>> question mark
>> if opt_str.is_some() {  }
>>
>> Problem is, this sounds scary to implement at the compiler level, if
>> it's possible at all :) I am just throwing it out there for others to
>> judge.
>>
>> Cheers
>>
>>
> I personally think a better solution is something like Haskell's do
> notation[1], where you can chain several computations that return
> Option<..> such that if any intermediate one returns None, the later ones
> are not evaluated and the whole expression returns None, which saves having
> to call .get()/.unwrap()/.expect() a lot.
>
> This can work for types like Result too (in fact, the Haskell
> implementation of `do` is sugar around some monad functions, so any monad
> can be used there; we currently don't have the power to express the monad
> typeclass/trait in Rust so the fully general form probably isn't possible
> as a syntax extension yet, although a limited version is).
>
>
> Huon
>
> [1]: http://en.wikibooks.org/wiki/Haskell/do_Notation
>
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Let’s avoid having both foo() and foo_opt()

2013-12-08 Thread Gábor Lehel
On Sun, Dec 8, 2013 at 10:26 AM, Ziad Hatahet  wrote:

> On Sat, Dec 7, 2013 at 2:21 PM, Gábor Lehel  wrote:
>
>> I do wonder whether it wouldn't make sense to add sugar for Option, at
>> least eventually. (`int?` at the type level is really nice, too... too bad
>> it doesn't play so well with Rust's sigils. Introducing the potential
>> confusion between `~?T` and `?~T` is probably a step too far.)
>>
>
> Wouldn't it be better to add something similar to Haskell's `do` instead
> of another sigil?
>
>
...that's basically what I was saying.



> --
> Ziad
>
>


-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Unbounded channels: Good idea/bad idea?

2013-12-19 Thread Gábor Lehel
I don't have any special expertise in or experience with this, I'm just
trying to draw logical inferences from what others have written.

Stated:

 - From a semantic perspective the only distinction is between bounded and
unbounded queues. The capacity of a bounded queue only makes a difference
with regards to performance.

 - There is also no such thing as an unbounded queue. The only distinction
is between queues whose capacities are constrained by us, and fail
predictably, and queues whose capacities are constrained by the available
resources, and fail chaotically.

 - The storage for a bounded queue need not all be allocated up-front, this
also just a performance optimization (or pessimization).

The big difference is in the user of the queue and the use case. Some users
in some cases don't want to have to think about the possibility of failure,
either because they believe that it's extremely unlikely or impossible (the
producer is only ever going to send 100 messages, no way it's going to
exhaust resources), and/or because failure is not a big problem for them
(we can just restart the thing, and putting in the effort to rule failure
out up-front doesn't pass the cost-benefit test). Other users in other
cases believe failure is a serious consideration and they want to be able
to prevent it or handle it well.

Assuming that we want to continue catering foremost to the people who don't
want to think about failure, but also don't want to leave the people who do
want to think about it out in the cold, what about:

 - Having only one type of queue, which is bounded,

 - and whose default capacity is just small enough that it would be hit
before exhausting resources, but is otherwise still ridiculously large
("effectively unbounded") (so basically what Kevin wrote),

 - failing the task on `send()` if the capacity would be exceeded,

 - allowing a smaller capacity (down to 1) to be specified instead,

 - also providing a try_send() method which returns false/Err, instead of
failing, when there is no available capacity,

 - and using a different representation for the queue storage for different
capacity classes: e.g. allocating up-front if it's "small" and growing it
incrementally if it's "large"?

The last point is speculative: I don't know if bounded and "effectively
unbounded" queues can both be implemented performantly in a single type.

Anyway: could this work?


On Thu, Dec 19, 2013 at 7:23 PM, Kevin Ballard  wrote:

> Here’s an example from where I use an infinite queue.
>
> I have an IRC bot, written in Go. The incoming network traffic of this bot
> is handled in one goroutine, which parses each line into its components,
> and enqueues the result on a channel. The channel is very deliberately made
> infinite (via a separate goroutine that stores the infinite buffer in a
> local slice). The reason it’s infinite is because the bot needs to be
> resilient against the case where either the consumer unexpectedly blocks,
> or the network traffic spikes. The general assumption is that, under normal
> conditions, the consumer will always be able to keep up with the producer
> (as the producer is based on network traffic and not e.g. a tight CPU loop
> generating messages as fast as possible). Backpressure makes no sense here,
> as you cannot put backpressure on the network short of letting the socket
> buffer fill up, and letting the socket buffer fill up with cause the IRC
> network to disconnect you. So the overriding goal here is to prevent
> network disconnects, while assuming that the consumer will be able to catch
> up if it ever gets behind.
>
> This particular use case very explicitly wants a dynamically-sized
> infinite channel. I suppose an absurdly large channel would be acceptable,
> because if the consumer ever gets e.g. 100,000 lines behind then it’s in
> trouble already, but I’d rather not have the memory overhead of a
> statically-allocated gigantic channel buffer.
>
> -Kevin
>
> On Dec 19, 2013, at 10:04 AM, Jason Fager  wrote:
>
> Okay, parallelism, of course, and I'm sure others.  Bad use of the word
> 'only'.  The point is that if your consumers aren't keeping up with your
> producers, you're screwed anyways, and growing the queue indefinitely isn't
> a way to get around that.  Growing queues should only serve specific
> purposes and make it easy to apply back pressure when the assumptions
> behind those purposes go awry.
>
>
> On Thursday, December 19, 2013, Patrick Walton wrote:
>
>> On 12/19/13 6:31 AM, Jason Fager wrote:
>>
>>> I work on a system that handles 10s of billions of events per day, and
>>> we do a lot of queueing.  Big +1 on having bounded queues.  Unbounded
>>> in-memory queues aren't, they just have a bound you have no direct
>>> control over and that blows up the world when its hit.
>>>
>>> The only reason to have a queue size greater than 1 is to handle spikes
>>> in the producer, short outages in the consumer, or a bit of
>>> out-of-phaseness between producers and con

Re: [rust-dev] Let’s avoid having both foo() and foo_opt()

2013-12-22 Thread Gábor Lehel
Using `match` works well enough, but if there's demand for a refutable
`let` which is lighter-weight, what about:

let Some(result) = from_utf8(some_bytes) else fail!();

In other words, if the `let` pattern is refutable, you have to provide
something `else` with return type `!` as the alternative for when the `let`
fails to match.


(I could imagine that being generalized to any number of `else`s of which
only the last "returns `!`" (i.o.w. never returns), for example

let Some(result) = from_utf8(some_bytes) else
from_utf8(some_other_bytes) else fail!();

and/or to allowing anything `else` which always matches, e.g.

let Some(result) = from_utf8(some_bytes) else Some("default");

of which anything that "returns" `!` would be only a special case. But
these have progressively diminishing returns, and I'm merely mentioning,
not endorsing them.)


On Tue, Dec 17, 2013 at 9:11 PM, Kevin Ballard  wrote:

> On Dec 17, 2013, at 11:37 AM, Stefan Plantikow 
> wrote:
>
> Hi,
>
> Am 17.12.2013 um 20:10 schrieb Corey Richardson :
>
> On Tue, Dec 17, 2013 at 2:06 PM, Stefan Plantikow
>  wrote:
>
> Hi,
>
>
> Am 09.12.2013 um 16:53 schrieb Damien Radtke :
>
> I have no idea if it would be feasible in the standard library, but
> wouldn't the ideal solution be having one function (e.g. from_utf8()) that
> could return two possible values, a bare result and an Option? Letting the
> compiler decide which version to use based on type inference like this:
>
>let result: ~str = from_utf8(...);
>let result: Option<~str> = from_utf8(...);
>
> Assuming both of them are passed invalid UTF8, then the first version
> would fail, but the second version would just return None.
>
>
>
>
> We already have pattern matching in `let` (the LHS is a pattern), but
> it's only for irrefutable patterns. IOW, `let` can never fail, and
> that's a very very useful property IMO.
>
>
> oh ok I haven’t kept up on the syntax then. Given the utility of
> destructuring bind for error handling, wouldn't it make sense to have a
> variant of let that can fail?
>
> Now syntax is a matter of practicality and taste but spontaneously this
> comes to mind:
>
>let opt Some(~result) = from_utf8(..)
>
> comes to mind.
>
>
> You can do it with a bit more verbosity, which I think is perfectly fine
> as it makes failure much more obvious.
>
> let result = match from_utf8(..) {
> Some(~result) => result,
> _ => fail!("b0rk b0rk b0rk")
> };
>
> Of course, in this particular example, you'd probably just write
>
> let result = from_utf8(..).unwrap();
>
> but the longer match form will work for other enums.
>
> -Kevin
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Let’s avoid having both foo() and foo_opt()

2013-12-23 Thread Gábor Lehel
On Mon, Dec 23, 2013 at 10:11 AM, Masklinn  wrote:

>
> On 2013-12-23, at 05:12 , Corey Richardson  wrote:
>
> > I find the ability to have refutable let
>
> I may have missed it, but is there a reason not to have just that? Make
> let similar to Erlang’s `=` and fail on refutation?
>

I don't like either that (a) the possible failure is silent, and refutable
lets look the same as irrefutable ones, nor (b) baking fail!() into the
semantics. Haskell has these also and I think it's a wart. The proposed
syntax solves both issues.

Rust doesn't allow partial matches in `match`es either, for reasons, so I
think it would be pretty strange to turn around and silently allow them in
`let`s.

The strongest arguments against IMHO are YAGNI (which is why I prefaced the
whole thing with *if* there's demand for a refutable let), and the
ambiguity with `if` (which is a technical issue that may or may not have a
satisfactory solution).

A meta-level process question: now that we have the `feature` attribute,
wouldn't that be a great way to empirically decide questions like these?
Implement the feature, put it behind a feature gate, and see how much use
it gets, and/or if there are any problems with it? This isn't the first
case where it seems like that might be more productive than trying to
anticipate all of the answers from theory alone. And it seems to work
pretty well for GHC. Just perhaps we could be more aggressive about
removing features which weren't successful.


>
> You said you find the basic let’s irrefutability to be a useful property
> but have not explained why, and why it makes sense to introduce a
> separate-but-similar construct with subtly different semantics
> (I also see this as a possible failure, would users of the language
> really expect `let` to be irrefutable and `let [else]` to be refutable,
> or would they expect `let [else]` to allow returning a value and `let`
> to just fail on mismatch? If such different semantics are desired,
> I’d suggest using a different keyword entirely)
>

If I'm understanding you correctly, this is a problem that would solve
itself: if someone thinks a plain `let` is refutable and will just fail on
mismatch, the compiler will quickly tell her otherwise.


>
> > more compelling than the
> > ability to work around it with functions wrapping `match`
>
> That assertion seems ill supported so far: just about every example is
> in terms of `Option`, and `Option` is the one type which does not need a
> refutable let, owing to its truckload of convenience methods covering
> just about every basic use cases the only reasons to use a `match` with
> Option are personal preferences and insufficient knowledge of the type.
>
> > On Sun, Dec 22, 2013 at 11:00 PM, Carter Schonwald
> >  wrote:
> >> Agreed!
> >>
> >>
> >> On Sunday, December 22, 2013, Ziad Hatahet wrote:
> >>>
> >>> But we already have Option::unwrap_or() and Option::unwrap_or_else()
> that
> >>> behave similar to the 'else' syntax suggested above.
> >>>
> >>> --
> >>> Ziad
> >>>
> >>>
> >>> On Sun, Dec 22, 2013 at 10:37 AM, Léo Testard 
> >>> wrote:
> >>>>
> >>>> Hello,
> >>>>
> >>>> Le 22 déc. 2013 à 18:59, Stefan Plantikow 
> a
> >>>> écrit :
> >>>>
> >>>>> Hi,
> >>>>>
> >>>>> Am 22.12.2013 um 16:47 schrieb Gábor Lehel :
> >>>>>
> >>>>> This is a nice idea.  At first I thought it wouldn’t work with `if`
> but
> >>>>> in expressions `if` requires `else` so the grammar wouldn’t be
> ambiguous:
> >>>>>
> >>>>
> >>>> No, it doesn't. As long as the if's "true block" returns unit.
> >>>> let foo = if ... { }; is perfectly legal, even it doesn't make much
> sense
> >>>> in practice.
> >>>>
> >>>> Leo
> >>>>
> >>>> ___
> >>>> Rust-dev mailing list
> >>>> Rust-dev@mozilla.org
> >>>> https://mail.mozilla.org/listinfo/rust-dev
> >>>>
> >>>
> >>
> >> ___
> >> Rust-dev mailing list
> >> Rust-dev@mozilla.org
> >> https://mail.mozilla.org/listinfo/rust-dev
> >>
> > ___
> > Rust-dev mailing list
> > Rust-dev@mozilla.org
> > https://mail.mozilla.org/listinfo/rust-dev
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Let’s avoid having both foo() and foo_opt()

2013-12-23 Thread Gábor Lehel
On Sun, Dec 22, 2013 at 7:37 PM, Léo Testard  wrote:

> Hello,
>
> Le 22 déc. 2013 à 18:59, Stefan Plantikow  a
> écrit :
>
> > Hi,
> >
> > Am 22.12.2013 um 16:47 schrieb Gábor Lehel :
> >
> > This is a nice idea.  At first I thought it wouldn’t work with `if` but
> in expressions `if` requires `else` so the grammar wouldn’t be ambiguous:
> >
>
> No, it doesn't. As long as the if's "true block" returns unit.
> let foo = if ... { }; is perfectly legal, even it doesn't make much sense
> in practice.
>
> Leo
>

Thinking about this, if we say that `else` following a `let` is allowed if
*and only if* the pattern is refutable, then this wouldn't actually be
ambiguous, because something of type `()` can never be refutable. Therefore
`let ... = if { ... } else { ... };` can only be legal if the `else`
belongs to the `if`.

I'm not completely clear on the relationship between grammatic and semantic
ambiguity. Would the grammar still be ambiguous in this case? I would
naively think that you're not allowed to take types and such into account
at the grammar level, but Rust seemingly already does so w.r.t. whether or
not things (such as `if`) return `()`, so I'm a little bit confused. Can
someone explain?



>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Let’s avoid having both foo() and foo_opt()

2013-12-23 Thread Gábor Lehel
That seems like a nice compromise, but I don't think it's a good one.
Either you intended the pattern to be refutable or you didn't. If you
didn't and it is, you should get an error, not a warning. If you did, you
shouldn't get a warning at all. Are you going to put a compiler pragma to
disable the warning at every use site where you intentionally want a let to
be refutable? At that point, you might as well have separate syntax for
refutable and irrefutable lets. Or are you just going to live with having
false positive warnings in your code?

(FWIW, I'm basically fine with having only irrefutable lets, and using
`match` for the other cases, or in other words the status quo. But if we
*do* add refutable lets, I strongly think they should be explicit.)


On Mon, Dec 23, 2013 at 7:03 PM, Carter Schonwald <
carter.schonw...@gmail.com> wrote:

> that seems like a reasonable balance
>
>
> On Mon, Dec 23, 2013 at 12:23 PM, Patrick Walton wrote:
>
>> On 12/23/13 4:12 AM, Gábor Lehel wrote:
>>
>>> I don't like either that (a) the possible failure is silent, and
>>> refutable lets look the same as irrefutable ones, nor (b) baking fail!()
>>> into the semantics. Haskell has these also and I think it's a wart. The
>>> proposed syntax solves both issues.
>>>
>>
>> For what it's worth, Rust's pattern matching is pretty heavily based on
>> OCaml's and the OCaml compiler complains with a warning if you use a
>> refutable pattern in `let`.
>>
>
>> Patrick
>>
>>
>> ___
>> Rust-dev mailing list
>> Rust-dev@mozilla.org
>> https://mail.mozilla.org/listinfo/rust-dev
>>
>
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Thoughts on the Rust Roadmap

2013-12-30 Thread Gábor Lehel
It would be helpful to know what the (rough) plan is for after 1.0. A
series of 1.x releases and later 2.0, or just 2.0? At what kind of time
scales? What kind of changes will be appropriate for each? (i.e. language
features, bugfixes, compatibility breaks of various forms in various places)

How is Rust going to feel about the prospect of backwards-incompatible
changes after 1.0? Like C++: never ever break anything ever ever?


On Mon, Dec 30, 2013 at 5:29 PM, Patrick Walton wrote:

> On 12/30/13 5:00 AM, Armin Ronacher wrote:
>
>> But there is so much more work needed.  Not just because the language is
>> lacking things that are needed, but because many users of the language
>> are not sure yet how to use it.
>>
>
> Documentation is independent of freezing the language definition.
>
>
>  The compiler and the standard library use widely different patterns and
>> so do libraries written by other people.
>>
>
> IMO, the compiler doesn't need to be updated to modern Rust before 1.0.
> (`javac` is still not updated to modern Java standards, for example.) The
> standard library does, however.
>
>
>  There are half a dozen ways to
>> deal with errors now, there are different patterns of how to deal with
>> Options and Results.
>>
>
> These need to be cleaned up before 1.0. Conditions in particular need to
> be removed.
>
>
>  There are vastly different ways to design whole
>> APIs, how to deal with the traits and so forth.
>>
>
> We'll standardize on something before 1.0. The traits need to be
> redesigned in general.
>
>
>  There are different
>> patterns to interface with native libraries, different patterns to
>> interface with task local data etc.
>>
>
> Are you referencing RAII versus `with_foo` style methods? The latter are
> deprecated and should be removed before 1.0.
>
>
>  The introduction of external iterators (which I found the most exciting
>> change of the language) has shown a whole new area of shortcomings in
>> the current feature set (lack decltype when returning composed iterators
>> for instance,
>>
>
> This is the first time I've heard of this as a missing feature, and I'm
> opposed. This would make typechecking significantly more complex.
>
>  non sendable closures etc.).
>>
>
> Is `proc` not sufficient? We could prioritize adding unboxed closures, but
> since they're backwards compatible as far as I know, I don't see a major
> need to add them before 1.0.
>
>
>  Not only did it show
>> shortcomings in lacking features, it also put some emphasis on new
>> patterns that are not widely deployed in the stdlib yet.
>>
>
> We need to add them before 1.0, but I personally don't see any major
> missing language features that we absolutely need to add, except those that
> are already on the roadmap.
>
>
>  I really hope there is left enough time to actually continue evolving
>> the language and libraries before freezing anything.  Especially now
>> that the split into libnative and libgreen is happening it will be
>> important to stay flexible for adjusting the design of the standard
>> library so that we don't end up with two vastly different ways to do IO.
>>
>
> We aren't going to freeze the entire standard library for all time with
> 1.0. Instead there will be "stability levels" like node.js (something like
> "frozen", "stabilizing", "unstable", "experimental"). I don't anticipate
> that all of libstd will be frozen before 1.0. Some modules will, and other
> modules will not.
>
>
>  As it stands right now, there
>> are too many things that make Rust still frustrating because you program
>> yourself into a corner.
>>
>
> It'd be best to file specific issues here. I'm sympathetic to wanting to
> adding more features if they're necessary, but none of the *specific*
> things mentioned in this post seem like blockers to me.
>
> In general I understand the concern, but there are also a huge number of
> people who just want the language to stabilize so that they can use it in
> production. The window for relevance is finite and we've been designing the
> language for a *long* time at this point.
>
> Patrick
>
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Future-proof for unboxed closures

2013-12-30 Thread Gábor Lehel
Wouldn't it have to be `&mut` rather than `&` to fit the semantics of | |,
which is affine and can mutate its environment?

And wouldn't this lead to divergence between the type- and value syntax,
with | | as a type being a boxed closure (`&mut FnMut`), and an unboxed
closure as a value? This was one of the nicer points of the recent closure
overhaul, and it would be a shame to lose it so soon.


On Mon, Dec 30, 2013 at 10:11 PM, Patrick Walton wrote:

> I've been thinking that to future-proof unboxed closures in the future we
> should maybe limit `|x| x+1` lambda syntax to either (a) require `&` in
> front or (b) in function position.
>
> So today you would write:
>
> let f = |x| x+1;
>
> But tomorrow you would write:
>
> let f = &|x| x+1;
>
> But it would always work here:
>
> v.map(|&x| x+1);
>
> The reason is simply that we'd like `|x| x+1` to become an unboxed closure
> in the future and it's easier in the language semantics to future-proof for
> it this way: we simply special-case the function argument position.
>
> Alternatively we can do it with assignability: say that `|x| x+1` is an
> anonymous type (an error today) that is assignable to the type
> `|int|->int`. That might be cleaner than special-casing the function
> argument position.
>
> Patrick
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Thoughts on the Rust Roadmap

2013-12-31 Thread Gábor Lehel
The other day on IRC `mitsuhiko` (who I believe is actually Armin, the OP
in this thread) wrote that he would like to be able to write something like

fn my_pipeline>(x: I) -> Iterator { ... }

and that got stuck in my head: maybe it's not such a crazy idea. The
objections to having type inference at the API level seem sound to me:
besides implementation difficulty, if an inferred return type can
potentially be a local or anonymous type from the body of the `fn`, how are
you supposed to know, from the outside, what you can *do* with that type? I
don't really like `decltype` solution either: it solves a problem, but
working with it everywhere is just painful, and it ties the exposed
interface too closely to the implementation. If you specify a trait, these
problems are solved: it's still an unknown type from the body of the `fn`,
but now the interface you are allowed to use with it is clear. This is what
traits are for in the first place. There's still the big question of what
this syntax would actually *mean*, however, and there are some things, like
the actual representation (incl. size and alignment) of the type, which
still wouldn't be determined by the function signature, and would depend on
the body of the function, and I don't know how problematic that is. I think
this also might be intimately related to the DST/SST discussion, because
it's basically an unboxed existential.

Speaking of which: I completely agree with Patrick that there's no point to
blocking Rust 1.0 on backwards compatible features. The reason I'm
nonetheless wary of a drive to finalize the language too soon is that there
are still some really huge things outstanding, such as DST/SST, that we
still need to figure out, let alone implement, and ideally after
implementation there should also be a cooling-off period to see if
everything's working well, and that there aren't further issues that end up
getting raised as a consequence (or if there are, how to address them), and
so forth.


On Tue, Dec 31, 2013 at 3:52 PM, Huon Wilson  wrote:

> On 01/01/14 01:43, Patrick Walton wrote:
>
>> On 12/31/13 6:09 AM, Huon Wilson wrote:
>>
>>> On 01/01/14 00:55, Armin Ronacher wrote:
>>>
 Hi,

 On 30/12/2013 17:29, Patrick Walton wrote:

> This is the first time I've heard of this as a missing feature, and I'm
> opposed. This would make typechecking significantly more complex.
>
 I'm not saying someone should add decltype :)  Just that from using
 the iterators we have now it becomes quite obvious that there are
 missing tools to use them to the fullest extend.

>>>
>> The question in my mind is whether all these things that we want are
>> backwards compatible, not whether they're nice to have. Anything backwards
>> compatible that is not already in the language is probably post-1.0 at this
>> point. *This is not because we don't want to add new features*—it's OK to
>> add new features to the language. Version 1.0 doesn't mean we are done and
>> the language is frozen for all time. It just means we're going to stop
>> making breaking language changes.
>>
>>  If we were to have unboxed closures similar to C++, where each closure
>>> is an (implicit) unique type, we'd likely need something like decltype
>>> (well, decltype(auto)) to make it possible to return them and things
>>> containing them, e.g. higher-order iterators like .map and .filter.
>>>
>>> (cc https://github.com/mozilla/rust/issues/3228 and
>>> https://github.com/mozilla/rust/issues/10448)
>>>
>>
>> Returning unboxed closures doesn't strike me as all that useful at the
>> moment, unless we add back capture clauses to allow values to be moved in.
>> Otherwise all you can refer to as upvars are passed-in parameters (due to
>> lifetime restrictions).
>>
>> In the event that you want to return closures that move in values, I
>> would say just use an explicit struct and a trait for now. We can revisit
>> this decision backwards compatibly if we need to in the future.
>>
>> Returning an iterator containing an unboxed closure that was passed in
>> doesn't require that feature, I don't think:
>>
>> pub fn map>(x: &[T], f: F) -> MapIterator { ... }
>>
>
> I mean
>
> fn my_pipeline>(x: I) -> MapIterator what_do_I_write_for_the_function_type_here>, and_again> {
>  x.filter(|un| boxed).map(|also| unboxed)
> }
>
> (where the function/closure is the second param to {Map,Filter}Iterator.)
>
> Huon
>
>
>> Patrick
>>
>> ___
>> Rust-dev mailing list
>> Rust-dev@mozilla.org
>> https://mail.mozilla.org/listinfo/rust-dev
>>
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Thoughts on the Rust Roadmap

2013-12-31 Thread Gábor Lehel
On Tue, Dec 31, 2013 at 4:41 PM, Daniel Micay  wrote:

> On Tue, Dec 31, 2013 at 10:39 AM, Gábor Lehel 
> wrote:
> >
> > Speaking of which: I completely agree with Patrick that there's no point
> to
> > blocking Rust 1.0 on backwards compatible features. The reason I'm
> > nonetheless wary of a drive to finalize the language too soon is that
> there
> > are still some really huge things outstanding, such as DST/SST, that we
> > still need to figure out, let alone implement, and ideally after
> > implementation there should also be a cooling-off period to see if
> > everything's working well, and that there aren't further issues that end
> up
> > getting raised as a consequence (or if there are, how to address them),
> and
> > so forth.
>
> Adding a backwards compatible feature can completely change the idioms
> of the language. Of course, if we're not worried about stabilizing the
> standard library then it doesn't matter.
>

Right. It was my understanding that we aren't.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] returning functions in rust

2014-01-11 Thread Gábor Lehel
On Fri, Jan 10, 2014 at 8:01 PM, Daniel Micay  wrote:

> On Fri, Jan 10, 2014 at 1:57 PM, Patrick Walton 
> wrote:
> > It doesn't exist, outside of traits. Unboxed closures will probably make
> it
> > possible to express once again though.
> >
> > Patrick
>
> The tricky part is the need to infer the return type if it's defined
> inside the function since it's different per-closure.
>

I wrote up a small proposal for a feature that could help with this:

https://github.com/mozilla/rust/issues/11455



> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Appeal for CORRECT, capable, future-proof math, pre-1.0

2014-01-12 Thread Gábor Lehel
On Sat, Jan 11, 2014 at 11:18 AM, Marijn Haverbeke wrote:

> I am not aware of an efficient way to provide
> automatic-overflow-to-bignum semantics in a non-garbage-collected
> language, without also imposing the burden of references/move
> semantics/etc on users of small integers. I.e. integers, if they may
> hold references to allocated memory can no longer sanely be considered
> a simple value type, which doesn't seem like it'd be a good idea for
> Rust.
>
> If there is a good solution to this, I'd love to find out about it.
>


This is a very good point. My thinking w.r.t. checking for overflow used to
be that if you're taking the performance hit for it, you might as well
expand to a big integer instead of failing, because if the use case is
indexing into an array, then the bounds check will catch it, and in other
use cases it's likely preferable.

But checked integers are POD and big/expanding ones aren't, which is a big
advantage for the former.

All of this really makes me wish hardware supported overflow checking in an
efficient manner natively. If it can throw an exception for divide by zero,
then why not overflow? (...I expect someone will actually have an answer
for this.)



> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Appeal for CORRECT, capable, future-proof math, pre-1.0

2014-01-13 Thread Gábor Lehel
Would it be accurate to say that the only significant changes required at
the language level for all this would be not having a default integer type,
and then probably polymorphic number literals a la Haskell? And that the
rest of this discussion is only about policy and ergonomics?


On Mon, Jan 13, 2014 at 2:30 AM, Patrick Walton wrote:

> On 1/12/14 5:22 PM, Daniel Micay wrote:
>
>> As far as I know, doing more takes way too long. Eliminating array
>> bounds checks and reasoning about arithmetic just doesn't really
>> happen.
>>
>
> I think the jury is still out. See "A Fast and Low-Overhead Technique to
> Secure Programs Against Integer Overflows" by Rodrigues et al., CGO '2013:
>
> http://homepages.dcc.ufmg.br/~fernando/publications/papers/
> CGO13_raphael.pdf
>
> This has been implemented as an LLVM pass:
>
> http://code.google.com/p/range-analysis/
>
> They cite a slowdown of merely 1.73% on the LLVM benchmark suite with
> their analysis, which is quite impressive. Given that range analysis can
> also eliminate bounds checks in Rust, I suspect that this is worth pursuing.
>
> That said I'm not sure we can gate 1.0 on this, since there's undoubtedly
> work needed to get this up to production quality. This is bleeding-edge
> stuff. However, I would personally definitely be interested in pursuing
> this post-1.0 for Servo and other apps that want to be hardened against
> overflows.
>
> Patrick
>
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: New Rust channel proposal

2014-01-24 Thread Gábor Lehel
On Fri, Jan 24, 2014 at 4:51 PM, Benjamin Striegel
wrote:

> No one's saying usability can't be discussed. If there are problems, we
> DESPERATELY DESPERATELY NEED to be made aware. Even if it's just "the names
> of these types confuse me", that's valuable feedback. But belaboring the
> point through this many mailing list posts just makes noise and wastes
> energy that the full-time devs (note: I am not a full-time dev) can spend
> on more valuable tasks.
>

The stronger claim that seems to be implied here (I'm not sure if you
intended it!) is that the role of "outside people" in discussing usability
issues should be confined to reporting problems. I don't agree with this.
At a minimum, even if the core developers do the deciding, a discussion
like this can provide them with ideas to choose from among or to draw
inspiration from, and gives them an informal survey of the preferences of
people who care enough to comment. Once in a while, a consensus may even
emerge (for example `box`).

When goals are clear, the collective can divide and conquer. But design and
> usability are subjective tasks, not objective ones, and instead of dividing
> and conquering we merely multiply and surrender.
>

The weaker claim is that "usability issues" can't be productively resolved
through a community process, and I'm not sure I agree with this either. Or
rather, I agree that if the only criterium that can be applied is personal
preference, this is true, but that doesn't hold in every case. For example,
words have meanings. In a debate about naming, it's very much possible to
bring arguments from reason concerning which names have meanings that best
align with the semantics of the construct in question. There may not always
be a clear winner (and then it's back to personal preference), but it's
worth looking for one.

(I agree that we have more important problems to solve than this, and that
the time of people who can is better spent building bikesheds than painting
them.)
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Future-proof for unboxed closures

2014-01-25 Thread Gábor Lehel
's not currently legal to follow an identifier
with a tuple type. (Correct me if I'm wrong.) This syntax would be
available both when declaring a generic type and when referring to one. (To
avoid confusion, we might want to require the same syntax be used
consistently for a given type, so that if you declare it with function-like
syntax, you also have to refer to it that way, and vice versa.) Then the
programmer can write decent-looking typedefs for whichever closure types
she will be using frequently. The standard library could provide a few
basic ones:

// yes, it would also work for traits!
trait Fn(Args...) -> Ret { ... }
trait FnMut(Args...) -> Ret { ... }
trait FnOnce(Args...) -> Ret { ... }

// so `MutFn(int, int) -> int` is the new `|int, int| -> int`, a slight
downgrade, in exchange for much flexibility elsewhere.
type MutFn(Args...) -> Ret = &mut FnMut(Args...) -> Ret;
type OnceFn(Args...) -> Ret = &move FnOnce(Args...) -> Ret;

// Would this still be important to have next to `OnceFn`? Anyway, it's
cute.
type Proc(Args...) -> Ret = ~FnOnce(Args...) -> Ret;



>
>
> Niko
>
> On Mon, Dec 30, 2013 at 07:31:45PM -0800, Patrick Walton wrote:
> > Yes, it would need to be &mut, you're right.
> >
> > I think the underlying type syntax would be something like
> `Fn` for the unboxed version, and `&mut Fn` for the
> boxed version. The type syntax with the bars is just syntactic sugar for
> the latter (and, in trait bound position, for the former).
> >
> > It's somewhat unfortunate but I don't see a particularly good
> alternative if we want boxed and unboxed closures alike to have
> nice-looking APIs. The alternative, I guess, is to block 1.0 on unboxed
> closures, convert all our APIs to unboxed closures where possible, and just
> say that if you want a boxed closure you have to write `&mut |x| x + 1` at
> each closure construction site...
> >
> > Patrick
> >
> > "Gábor Lehel"  wrote:
> > >Wouldn't it have to be `&mut` rather than `&` to fit the semantics of |
> > >|,
> > >which is affine and can mutate its environment?
> > >
> > >And wouldn't this lead to divergence between the type- and value
> > >syntax,
> > >with | | as a type being a boxed closure (`&mut FnMut`), and an unboxed
> > >closure as a value? This was one of the nicer points of the recent
> > >closure
> > >overhaul, and it would be a shame to lose it so soon.
> > >
> > >
> > >On Mon, Dec 30, 2013 at 10:11 PM, Patrick Walton
> > >wrote:
> > >
> > >> I've been thinking that to future-proof unboxed closures in the
> > >future we
> > >> should maybe limit `|x| x+1` lambda syntax to either (a) require `&`
> > >in
> > >> front or (b) in function position.
> > >>
> > >> So today you would write:
> > >>
> > >> let f = |x| x+1;
> > >>
> > >> But tomorrow you would write:
> > >>
> > >> let f = &|x| x+1;
> > >>
> > >> But it would always work here:
> > >>
> > >> v.map(|&x| x+1);
> > >>
> > >> The reason is simply that we'd like `|x| x+1` to become an unboxed
> > >closure
> > >> in the future and it's easier in the language semantics to
> > >future-proof for
> > >> it this way: we simply special-case the function argument position.
> > >>
> > >> Alternatively we can do it with assignability: say that `|x| x+1` is
> > >an
> > >> anonymous type (an error today) that is assignable to the type
> > >> `|int|->int`. That might be cleaner than special-casing the function
> > >> argument position.
> > >>
> > >> Patrick
> > >> ___
> > >> Rust-dev mailing list
> > >> Rust-dev@mozilla.org
> > >> https://mail.mozilla.org/listinfo/rust-dev
> > >>
> >
> > --
> > Sent from my Android phone with K-9 Mail. Please excuse my brevity.
>
> > ___
> > Rust-dev mailing list
> > Rust-dev@mozilla.org
> > https://mail.mozilla.org/listinfo/rust-dev
>
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Idea: Memcpyed stacks for green tasks

2014-01-26 Thread Gábor Lehel
On Sun, Jan 26, 2014 at 1:26 PM, Matthieu Monrocq <
matthieu.monr...@gmail.com> wrote:

>
>
>
> On Sun, Jan 26, 2014 at 3:31 AM, Brian Anderson wrote:
>
>> On 01/25/2014 08:58 AM, Bill Myers wrote:
>>
>>> Stack management for green tasks has been based in the past first on
>>> segmented stacks and then on standard large stacks.
>>>
>>> However, I just realized that there is a third alternative which might
>>> well be better than both of those.
>>>
>>> The idea is very simple: a green task would run on a large stack like
>>> now, but when it is descheduled, a memory buffer of the size of its used
>>> stack space is allocated, and its stack data is copied there; when it is
>>> rescheduled, the stack data is copied back to the original address.
>>>
>>
>> That's a really clever idea. There are though a number of situations
>> where we unsafely read from or write into other task's stacks that would
>> need to be considered carefully.
>>
>>
> Would it be possible to statically eliminate this risk by preventing the
> promotion of such affected tasks as greenlets ? Or is there today too many
> ways to read into a greenlet stack ?
>

We *already* statically prevent tasks from reading/writing each others'
stacks, the issue here is code which uses `unsafe { }` and does it anyways,
based on particular assumptions about how stacks are implemented which
would no longer be valid. The solution would be to (considering it
carefully) fix this unsafe code to no longer rely on these assumptions.


>
> -- Matthieu
>
>
>
>>
>> ___
>> Rust-dev mailing list
>> Rust-dev@mozilla.org
>> https://mail.mozilla.org/listinfo/rust-dev
>>
>
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Idea: Memcpyed stacks for green tasks

2014-01-26 Thread Gábor Lehel
On Sat, Jan 25, 2014 at 9:26 PM, Vadim  wrote:

> Hi Bill,
> If I understand this right, what you are proposing is the approach of
> Python's greenlets library, which I already tried to pitch 
> here.
>
>
> I think that the biggest problem with this approach, in its' raw form, is
> that greenlets are bound to a specific OS thread, which would cause all
> sorts of headache.  For example:
> - It would be difficult to balance greenlet tasks between several OS
> threads,
> - If async I/O requests for two greenlets, bound to the same OS thread,
> were to complete simultaneously, you would not be able to resume them in
> parallel,
> - If a thread were to become blocked for a significant amount of time, all
> the greenlets bound to it become un-resumable,
> and so on...
>
> Greelets would be much more palatable if their stacks were
> position-independent.  This might be possible to achieve with a suitable
> LLVM 
> transform,
> though I am not yet sure how portable this would be.
>

Totally not an expert in this area, but I'm wondering two things:

 - Once you hoist all variables whose address is taken out into this
secondary stack frame, if you have a pointer on the stack to an object on
the stack who *also*, in turn, has its address taken, then I believe you
would end up with a pointer in the secondary stack frame to an object in
the secondary stack frame, i.e. the secondary stack frame itself would not
be position independent. Does the secondary stack frame always stay in
place, so we don't really care about this?

 - Instead of heap-allocating a secondary stack frame, could another
potential solution be to use similar infrastructure as for DWARF-based
unwinding and/or precise GC tracing of the stack, in other words to emit a
side table of the positions of pointers into the stack in each stack frame,
and then when relocating the stack, to consult this side table and manually
fix-up all of the affected pointers? In this case, aside from the presence
of the side tables, the cost is borne only when actually relocating the
stack.



>
>
> cheers,
> Vadim
>
>
>
> On Sat, Jan 25, 2014 at 8:58 AM, Bill Myers wrote:
>
>> Stack management for green tasks has been based in the past first on
>> segmented stacks and then on standard large stacks.
>>
>> However, I just realized that there is a third alternative which might
>> well be better than both of those.
>>
>> The idea is very simple: a green task would run on a large stack like
>> now, but when it is descheduled, a memory buffer of the size of its used
>> stack space is allocated, and its stack data is copied there; when it is
>> rescheduled, the stack data is copied back to the original address.
>>
>> The copying can be performed either by memcpy (perhaps with a specialized
>> memcpy that assumes alignment and always uses SIMD instruction to copy with
>> no checks), or perhaps by using a compression algorithm designed for
>> maximum speed, such as Snappy or maybe a new ad-hoc design.
>>
>> This allows to have the best possible memory efficiency and thus the
>> maximum possible number of tasks, even better than segmented stacks due to
>> precise sizing and optional compression, while not adding any overhead to
>> code that does not block, either in terms of code generation complexity or
>> runtime cost.
>>
>> There is of course an additional runtime cost when blocking, but blocking
>> already often involves both system calls and context switching, and
>> workloads with lots of blocking green tasks are probably I/O bound anyway;
>> furthermore, stack data is probably in the CPU cache, so there shouldn't be
>> much penalty.
>>
>> The only significant drawback is that two green tasks running from the
>> same "large" stack (note that stacks cannot be easily switched, as that
>> would invalidate borrowed pointers on the stack to the stack) cannot
>> ever run concurrently; however, by making the number of large stacks a
>> large enough multiple of the number of CPU cores, the probability of
>> reduced parallelism due to this issue can be made as small as desired.
>>
>> In general, one would use an adaptive approach, where this kind of
>> copying would start to kick in only once the number of green tasks becomes
>> large; when not copying, one would just optionally use
>> madvise(MADV_DONTNEED) to trim unused whole pages of the stack.
>>
>> Also, if the data to be copied is large (several OS pages), one may use
>> mremap() or similar facilities to move the address space mappings instead
>> of the data itself.
>>
>> Some unsafe code that assumes that tasks can access each other's stacks
>> will break (probably only the new Mutex implementation), but that can be
>> fixed by putting the data in the task structure instead of the stack.
>>
>> Overall, this should allow writing something like a parallel network
>> server or web crawler in Rus

Re: [rust-dev] Idea: Memcpyed stacks for green tasks

2014-01-26 Thread Gábor Lehel
On Sun, Jan 26, 2014 at 2:02 PM, Gábor Lehel  wrote:

>
>
>
> On Sat, Jan 25, 2014 at 9:26 PM, Vadim  wrote:
>
>> Hi Bill,
>> If I understand this right, what you are proposing is the approach of
>> Python's greenlets library, which I already tried to pitch 
>> here<http://mail.mozilla.org/pipermail/rust-dev/2013-November/006544.html>.
>>
>>
>> I think that the biggest problem with this approach, in its' raw form, is
>> that greenlets are bound to a specific OS thread, which would cause all
>> sorts of headache.  For example:
>> - It would be difficult to balance greenlet tasks between several OS
>> threads,
>> - If async I/O requests for two greenlets, bound to the same OS thread,
>> were to complete simultaneously, you would not be able to resume them in
>> parallel,
>> - If a thread were to become blocked for a significant amount of time,
>> all the greenlets bound to it become un-resumable,
>> and so on...
>>
>> Greelets would be much more palatable if their stacks were
>> position-independent.  This might be possible to achieve with a suitable
>> LLVM 
>> transform<http://lists.cs.uiuc.edu/pipermail/llvmdev/2014-January/069662.html>,
>> though I am not yet sure how portable this would be.
>>
>
> Totally not an expert in this area, but I'm wondering two things:
>
>  - Once you hoist all variables whose address is taken out into this
> secondary stack frame, if you have a pointer on the stack to an object on
> the stack who *also*, in turn, has its address taken, then I believe you
> would end up with a pointer in the secondary stack frame to an object in
> the secondary stack frame, i.e. the secondary stack frame itself would not
> be position independent. Does the secondary stack frame always stay in
> place, so we don't really care about this?
>

(Thinking about this further, this was a silly question, because the
primary stack frame itself would have pointers into the secondary one, so
_obviously_ the latter cannot move.)
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Future-proof for unboxed closures

2014-01-27 Thread Gábor Lehel
On Mon, Jan 27, 2014 at 12:03 PM, Niko Matsakis  wrote:

> On Sat, Jan 25, 2014 at 07:07:32PM +0100, Gábor Lehel wrote:
> > If you wanted to pass an unboxed closure without indirection though, like
> > `fn foo>(x: T)`, then you would have to explicitly
> dereference
> > the closure, i.e. `foo(*|u| r)` (which might be OK).
>
> Why would you want to do that? There is no advantage to doing so; the ABI
> will almost certainly reintroduce indirection anyhow.
>

I think this question has a more general form, namely: when should I pass
by value and when using &move/&my? I expect this will come up quite a bit
if we add the latter.


>
> > The basic problem is that there's a zoo of closure types:
> > http://glaebhoerl.tumblr.com/rust_closure_types (I will be referring to
> > things from this in the following!)
>
> No time to write a long response here just now, but this is a very
> nice writeup, thanks.
>
>
>
> Niko
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Compile-time function evaluation in Rust

2014-01-29 Thread Gábor Lehel
On Wed, Jan 29, 2014 at 10:19 AM, Pierre Talbot  wrote:

> I add: a pure function can call extern code and diverges, so the pure
> requirements are a subset of the CTFE requirements.
>

How do you plan to enforce that a function won't diverge?

Are there any systems which allow calling functions and enforce convergence
without using dependent types?

(We could probably enforce it by prohibiting both mutual and self-recursion
as well as loops, but I'm guessing this would be too restrictive to be
acceptable.)



>
> Pierre
>
> ___
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Compile-time function evaluation in Rust

2014-01-29 Thread Gábor Lehel
Ah, so `fail!()` is not okay, but `loop { }` is okay.


On Wed, Jan 29, 2014 at 11:26 AM, Pierre Talbot  wrote:

> It's a diverging function in the sense of the manual:
> http://static.rust-lang.org/doc/master/rust.html#diverging-functions
>
> On 01/29/2014 11:21 AM, Gábor Lehel wrote:
>
>> On Wed, Jan 29, 2014 at 10:19 AM, Pierre Talbot > ptal...@hyc.io>> wrote:
>>
>> I add: a pure function can call extern code and diverges, so the
>> pure requirements are a subset of the CTFE requirements.
>>
>>
>> How do you plan to enforce that a function won't diverge?
>>
>> Are there any systems which allow calling functions and enforce
>> convergence without using dependent types?
>>
>> (We could probably enforce it by prohibiting both mutual and
>> self-recursion as well as loops, but I'm guessing this would be too
>> restrictive to be acceptable.)
>>
>>
>> Pierre
>>
>> ___
>> Rust-dev mailing list
>> Rust-dev@mozilla.org <mailto:Rust-dev@mozilla.org>
>> https://mail.mozilla.org/listinfo/rust-dev
>>
>>
>>
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Using Default Type Parameters

2014-02-02 Thread Gábor Lehel
On Mon, Feb 3, 2014 at 7:55 AM, Corey Richardson  wrote:

> Default typarams are awesome, but they're gated, and there's some
> concern that they'll interact unpleasantly with extensions to the type
> system (most specifically, I've seen concern raised around HKT, where
> there is conflicting tension about whether to put the "defaults" at
> the start or end of the typaram list).
>

Just for reference, this was discussed here:
https://github.com/mozilla/rust/pull/11217

(The tension is essentially that with default type args you want to put the
"least important" types at the end, so they can be defaulted, while with
HKT you want to put them at the front, so they don't get in the way of
abstracting over the important ones.)
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Proposal: Change Parametric Polymorphism Declaration Syntax

2014-02-03 Thread Gábor Lehel
Just because Any is a trait doesn't mean it doesn't break parametricity.
Look at this:

http://static.rust-lang.org/doc/master/src/std/home/rustbuild/src/rust-buildbot/slave/doc/build/src/libstd/any.rs.html#37-63

Because we have `impl Any for T`, it can be used with *any
type* (except borrowed data), including type parameters, whether or not
they declare the `T: Any` bound explicitly (which is essentially redundant
in this situation).

The proper thing would be for the compiler to generate an `impl Any for
MyType` for each individual type separately, rather than a single generic
impl which is valid for all types.

I also think we should guarantee parametricity for safe code and make
`size_of` an unsafe fn. Its legitimate uses in unsafe code (e.g. smart
pointers) are well encapsulated and don't expose parametricity violations,
and I don't believe safe code has a legitimate reason to use it (does it?).


On Sun, Feb 2, 2014 at 3:27 AM, Eric Reed  wrote:

> I'm going to respond to Any and size_of separately because there's a
> significant difference IMO.
>
> It's true that Any and trait bounds on type parameters in general can let
> function behavior depend on the passed type, but only in the specific
> behavior defined by the trait. Everything that's not a trait function is
> still independent of the passed type (contrast this with a setup where this
> wasn't true. `fn foo() -> int' could return 2i for int and spin up a
> tetris game then crash for uint). Any just happens to be powerful enough to
> allow complete variance, which is expected since it's just dynamic typing,
> but there's an important distinction still: behavior variance because of
> Any *is* part of the function because you need to do explicit type tests.
>
> I wasn't aware of mem::size_of before, but I'm rather annoyed to find out
> we've started adding bare A -> B functions since it breaks parametricity.
> I'd much rather put size_of in a trait, at which point it's just a weaker
> version of Any.
> Being able to tell how a function's behavior might vary just from the type
> signature is a very nice property, and I'd like Rust to keep it.
>
> Now, onto monomorphization.
> I agree that distinguishing static and dynamic dispatch is important for
> performance characterization, but static dispatch != monomorphization (or
> if it currently does, then it probably shouldn't) because not all
> statically dispatched code needs to be monomorphizied. Consider a function
> like this:
>
> fn foo(ox: Option<~A>, f: |~A| -> ~B) -> Option<~B> {
> match ox {
> Some(x) => Some(f(x)),
> None => None,
> }
> }
>
> It's quite generic, but AFAIK there's no need to monomorphize it for
> static dispatch. It uses a constant amount of stack space (not counting
> what `f' uses when called) and could run the exact same code for any types
> A or B (check discriminant, potentially call a function pointer, and
> return). I would guess most cases require monomorphization, but I consider
> universal monomorphization a way of implementing static dispatch (as
> opposed to partial monomorphization).
> I agree that understanding monomorphization is important for understanding
> the performance characteristics of code generated by *rustc*, but rustc !=
> Rust.
> Unless universal monomorphization for static dispatch makes its way into
> the Rust language spec, I'm going to consider it an implementation detail
> for rustc.
>
>
>
> On Sat, Feb 1, 2014 at 3:31 PM, Corey Richardson  wrote:
>
>> On Sat, Feb 1, 2014 at 6:24 PM, Eric Reed 
>> wrote:
>> > Responses inlined.
>> >
>> >>
>> >> Hey all,
>> >>
>> >> bjz and I have worked out a nice proposal[0] for a slight syntax
>> >> change, reproduced here. It is a breaking change to the syntax, but it
>> >> is one that I think brings many benefits.
>> >>
>> >> Summary
>> >> ===
>> >>
>> >> Change the following syntax:
>> >>
>> >> ```
>> >> struct Foo { ... }
>> >> impl Trait for Foo { ... }
>> >> fn foo(...) { ... }
>> >> ```
>> >>
>> >> to:
>> >>
>> >> ```
>> >> forall struct Foo { ... }
>> >> forall impl Trait for Foo { ... }
>> >> forall fn foo(...) { ... }
>> >> ```
>> >>
>> >> The Problem
>> >> ===
>> >>
>> >> The immediate, and most pragmatic, problem is that in today's Rust one
>> >> cannot
>> >> easily search for implementations of a trait. Why? `grep 'impl Clone'`
>> is
>> >> itself not sufficient, since many types have parametric polymorphism.
>> Now
>> >> I
>> >> need to come up with some sort of regex that can handle this. An easy
>> >> first-attempt is `grep 'impl(<.*?>)? Clone'` but that is quite
>> >> inconvenient to
>> >> type and remember. (Here I ignore the issue of tooling, as I do not
>> find
>> >> the
>> >> argument of "But a tool can do it!" valid in language design.)
>> >
>> >
>> > I think what I've done in the past was just `grep impl | grep Clone'.
>> >
>> >>
>> >> A deeper, more pedagogical problem, is the mismatch between how `struct
>> >> Foo<...> { ... }` is read and how it 

Re: [rust-dev] Using Default Type Parameters

2014-02-03 Thread Gábor Lehel
Possibly, but it's not particularly well-trodden ground (I think Ur/Web
might have something like it?).

And would you really want to write `HashMap`?


On Mon, Feb 3, 2014 at 6:34 PM, Matthieu Monrocq  wrote:

>
>
>
> On Mon, Feb 3, 2014 at 8:41 AM, Gábor Lehel  wrote:
>
>> On Mon, Feb 3, 2014 at 7:55 AM, Corey Richardson wrote:
>>
>>> Default typarams are awesome, but they're gated, and there's some
>>> concern that they'll interact unpleasantly with extensions to the type
>>> system (most specifically, I've seen concern raised around HKT, where
>>> there is conflicting tension about whether to put the "defaults" at
>>> the start or end of the typaram list).
>>>
>>
>> Just for reference, this was discussed here:
>> https://github.com/mozilla/rust/pull/11217
>>
>> (The tension is essentially that with default type args you want to put
>> the "least important" types at the end, so they can be defaulted, while
>> with HKT you want to put them at the front, so they don't get in the way of
>> abstracting over the important ones.)
>>
>>
> Thinking out loud: could parameters be "keyed", like named functions
> arguments ? If they were, then their position would matter little.
>
> -- Matthieu
>
>
>> ___
>> Rust-dev mailing list
>> Rust-dev@mozilla.org
>> https://mail.mozilla.org/listinfo/rust-dev
>>
>>
>
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


  1   2   >