Re: [rust-dev] Higher-Kinded Types vs C++ Combos
(Another big reason I like forums over mailing lists, which I forgot to mention in that other thread, is that I can fix my mistakes!) Typo correction: struct ListTrait { templatetypename T typedef listT collection; }; And I'd rephrase my third question: 3. If Rust had C++-style typedefs, would it make sense to argue that we don't need Higher-Kinded Types, we can just use typedefs for everything? In case people are finding the Combo concept hard to follow, I will offer GiST (http://en.wikipedia.org/wiki/GiST) as a concrete example. A GiST has a few different parts, each of which can be swapped out to produce different kinds of trees. When I tried to implement the GiST in C#, I found that I wanted 8 type parameters on the GiST base class: - The derived tree type - The internal node type - The leaf node type - The compressed left entry type - The uncompressed leaf entry type - The compressed internal entry type - The uncompressed internal entry type - The key type (type of data stored in an entry) This, of course, is utterly impractical when they have to be listed repeatedly, every time the base class is mentioned. I figured out how to whittle it down to one parameter on the base class and two elsewhere, but I had to restructure the code in some unnatural ways, so the readability of the code was substantially harmed, and there was some performance cost as well (some interfaces, some virtual methods, more casts.) Even with one or two type parameters, the names of things were still unweildy because often the concrete type parameters were themselves parameterized. Of course, type parameters are not the only way to do a GiST. It can also be done by defining everything in terms of interfaces (or in Rust, trait objects). But this implies that when the different parts of the code interact, they must use dynamic dispatch. Even worse, although the entries (leaf/internal, compressed/uncompressed) are small implicitly-copyable data types (let's say 8 - 32 bytes typically, though I don't have the code handy), most of the code will be unaware how large or small each entry is, so the entries would (in C# at least) have to be stored on the heap, rather than passed around directly as structs (I'm not sure what you'd do in Rust). Thus, if you want to implement a GiST in C# you get either hard-to-follow code with sub-optimal performance, or straightforward code with terrible performance. The third alternative, of course, is to not implement a GiST, but implement each kind of tree (B+ tree, R-tree, ...) separately. This requires code duplication and repeated design work, the elimination of which was the reason GiSTs were invented in the first place. In Rust, I assume you could use macros to overcome some of the problems that make a GiST difficult to do in C#. However, I am thinking that if typedef is in some ways a more powerful concept than HKT, arguably Rust should support them first or investigate whether they can be generalized to do what HKTs were meant to do. On Sat, Dec 7, 2013 at 12:10 AM, David Piepgrass qwertie...@gmail.comwrote: 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 template typename class collection struct Numbers { collectionint integers; collectionfloat floats; }; So then you can write Numbersvector for a structure that contains vectorT collections, and Numberslist for a structure that contains listT collections. EXCEPT that it doesn't actually work, because vectorT 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 { templatetypename T struct collection { typedef vectorT type; }; }; struct ListTrait { templatetypename T struct collection { typedef listT type; }; }; templatetypename Traits struct Numbers { Traits::collectionint::type integers; Traits::collectionfloat::type floats; }; // Use NumbersVectorTrait for vectorT, NumbersListTrait for listT. This is clunkier, but it would have been a bit simpler if C++ supported templatized typedefs: struct VectorTrait { templatetypename T typedef vectorT collection; }; struct ListTrait { templatetypename T typedef vectorT collection; }; templatetypename Traits struct Numbers { Traits::collectionint
[rust-dev] Higher-Kinded Types vs C++ Combos
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 template typename class collection struct Numbers { collectionint integers; collectionfloat floats; }; So then you can write Numbersvector for a structure that contains vectorT collections, and Numberslist for a structure that contains listT collections. EXCEPT that it doesn't actually work, because vectorT 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 { templatetypename T struct collection { typedef vectorT type; }; }; struct ListTrait { templatetypename T struct collection { typedef listT type; }; }; templatetypename Traits struct Numbers { Traits::collectionint::type integers; Traits::collectionfloat::type floats; }; // Use NumbersVectorTrait for vectorT, NumbersListTrait for listT. This is clunkier, but it would have been a bit simpler if C++ supported templatized typedefs: struct VectorTrait { templatetypename T typedef vectorT collection; }; struct ListTrait { templatetypename T typedef vectorT collection; }; templatetypename Traits struct Numbers { Traits::collectionint integers; Traits::collectionfloat floats; }; // Now write NumbersVectorTrait instead of Numbersvector, // NumbersListTrait instead of Numberslist. 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 ConcreteATraits A; typedef ConcreteBTraits B; typedef ConcreteCTraits C; }; templatetypename Combo class ConcreteA { Combo::B* b; ... }; templatetypename Combo class ConcreteB { Combo::C* c; ... }; templatetypename Combo class ConcreteC { Combo::A* b; ... }; Here I've created a network of types (ConcreteAMyCombo, ConcreteBMyCombo, and ConcreteCMyCombo) 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@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Persistent data structures
My next goal is a persistent tree-map, probably cribbing from Haskell's Data.Map. I look forward to hearing how that goes! I've been meaning to make a data structure in Rust too, but it's hard to find the time, so how's about I tell you guys about it instead. I call my data structure an optionally-persistent hashtable or hashset. In C# I implemented this optionally-persistent hashtable and hashset with mutable and immutable variants of each (the term semi-persistent was already taken and means something else). I've been meaning to write an article about this, but didn't get around to it yet. Optionally-persistent means that it's structured as if it were persistent, but each node can be either mutable (locally) or immutable (recursively). Each node has a frozen flag which implicitly applies recursively to all children. Converting the tree from immutable to mutable, or mutable to immutable takes O(1) time. Immutable to mutable is essentially a no-op (the mutable copy has copy-on-write behavior), while mutable-to-immutable simply requires marking the root node as frozen. The hashtable is really a tree that is up to 8 levels deep, with each level representing 4 bits of the hashcode (not sure if this is the best approach). Lots more details in the doc comment: https://sourceforge.net/p/loyc/code/HEAD/tree/Src/Loyc.Collections/Sets/InternalSet.cs My benchmarks show that mutating such a set/map is dramatically faster than immutable editing (which requires copying multiple nodes for each change), and not that much slower than a traditional hashtable, so I think it's hands down superior to a traditional persistent hash tree. In my version, from the end-user's perspective, there's a separate data type for immutable and mutable versions of the data structure (MMapT and MSetT are mutable, MapT and SetT are immutable). Both data types encapsulate an instance of InternalSetT which is the real data structure. InternalSetT manages a tree of nodes, where each node has 16 entries and represents 4 bits of the hashcode. There's also an interesting variation called InvertibleSetT; an invertible set can represent a negative set such as all integers except 0 and 1. -- - David http://loyc-etc.blogspot.com ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Persistent data structures
Please disregard this message; I hadn't seen Bill Myers' solution (copy-on-write by cloning only when reference count 1), which sounds like it's probably perfect for Rust. On Wed, Dec 4, 2013 at 9:03 PM, David Piepgrass qwertie...@gmail.comwrote: My next goal is a persistent tree-map, probably cribbing from Haskell's Data.Map. I look forward to hearing how that goes! I've been meaning to make a data structure in Rust too, but it's hard to find the time, so how's about I tell you guys about it instead. I call my data structure an optionally-persistent hashtable or hashset. In C# I implemented this optionally-persistent hashtable and hashset with mutable and immutable variants of each (the term semi-persistent was already taken and means something else). I've been meaning to write an article about this, but didn't get around to it yet. Optionally-persistent means that it's structured as if it were persistent, but each node can be either mutable (locally) or immutable (recursively). Each node has a frozen flag which implicitly applies recursively to all children. Converting the tree from immutable to mutable, or mutable to immutable takes O(1) time. Immutable to mutable is essentially a no-op (the mutable copy has copy-on-write behavior), while mutable-to-immutable simply requires marking the root node as frozen. The hashtable is really a tree that is up to 8 levels deep, with each level representing 4 bits of the hashcode (not sure if this is the best approach). Lots more details in the doc comment: https://sourceforge.net/p/loyc/code/HEAD/tree/Src/Loyc.Collections/Sets/InternalSet.cs My benchmarks show that mutating such a set/map is dramatically faster than immutable editing (which requires copying multiple nodes for each change), and not that much slower than a traditional hashtable, so I think it's hands down superior to a traditional persistent hash tree. In my version, from the end-user's perspective, there's a separate data type for immutable and mutable versions of the data structure (MMapT and MSetT are mutable, MapT and SetT are immutable). Both data types encapsulate an instance of InternalSetT which is the real data structure. InternalSetT manages a tree of nodes, where each node has 16 entries and represents 4 bits of the hashcode. There's also an interesting variation called InvertibleSetT; an invertible set can represent a negative set such as all integers except 0 and 1. -- - David http://loyc-etc.blogspot.com -- - David http://qism.blogspot.com ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Rust forum
David Piepgrass qwertie...@gmail.com wrote: Okay, well, I've never liked mailing lists at all, because: 1. In non-digest mode, My inbox gets flooded. 2. In digest mode, it's quite inconvenient to write a reply, having to cut out all the messages that I don't want to reply to and manually edit the subject line. Also, unrelated messages are grouped together while threads are broken apart, making discussions harder to follow. 3. In email I don't get a threaded view. If I go to mailing list archives to see a threaded view, I can't reply. Just use NNTP (gmane.comp.lang.rust.devel on news.gmane.org). Most newsreader support threaded view (Thunderbird does). You can configure the mailing list s.t. mails are not actually delivered to your email address even if you are registered. But looking at your email address you are already using gmane. I'm using gmail; I have never used gmane and know nothing about it. I also don't have or use an email client or a newsreader. And there are lots of people like me. If gmane is the preferred way to interact with this mailing list, users clicking on the mailing list link: https://mail.mozilla.org/listinfo/rust-dev should be pointed at gmane specifically. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Rust forum
Hey, why not set up a Discourse forum? That would be so. much. better. than a mailing list. As an OSS dev I've been itching to get one myself, but don't have time or much money to set it up. For Mozilla, though? No problem I'm sure. http://www.discourse.org/ Google: discourse hosting. http://www.google.com/search?q=discourse+hosting From: Eric Reed ecr...@cs.washington.edu ... I'm not aware of any plans for a rust-users forum. Maybe spinning a rust-users mailing list off from rust-dev would make sense? We already did a similar thing with our IRC channels. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Rust forum
On 02/12/2013 16:21, David Piepgrass wrote: That would be so. much. better. than a mailing list. Hi. Could you expand on this? I don?t necessarily disagree, but as the one proposing change it?s up to you to convince everyone else :) -- Simon Sapin Okay, well, I've never liked mailing lists at all, because: 1. In non-digest mode, My inbox gets flooded. 2. In digest mode, it's quite inconvenient to write a reply, having to cut out all the messages that I don't want to reply to and manually edit the subject line. Also, unrelated messages are grouped together while threads are broken apart, making discussions harder to follow. 3. In email I don't get a threaded view. If I go to mailing list archives to see a threaded view, I can't reply. 4. I have to manually watch for replies to my messages or to threads I'm following. If someone mentions my name (not that they would), I won't be notified. In contrast, Discourse has a variety of email notification options. I don't know if those options are enough to please everybody, but you can probably configure it to notify you about all posts, which makes it essentially equivalent to a mailing list. It supports reply by email, so those that prefer a mailing list can still pretend it's a mailing list. Currently I'm getting an shrunk digest of Discourse Meta--by email I only get a subset of all messages, auto-selected by Discourse, whatever it thinks is interesting. That's good for me: I really don't want to see every message. Plus, a mailing list offers less privacy as it mandates publishing your email address. That's not a big deal for me personally, but do you really want to require that from every Rust user? (btw, if I'm wrong about any of the above points, I promise there are lots of other netizens out there who have the same misconception(s), so many of them will avoid mailing lists. The fact that y'all are talking to me on a mailing list suggests that the disadvantages of a mailing list are not a big deal *to you*, but as for those who aren't participating, you can't conclude *they* prefer mailing lists.) And like mailing lists, Discourse also supports private messages. I don't understand why Paul mentioned GPG. You want to encrypt messages to a public mailing list? You can sign messages, but surely almost no one actually checks the signature, and I'd be surprised if Discourse didn't offer some built-in evidence of identity (surely it's not like email in letting you spoof the sender name easily?). I heard discourse supports attachments, just that you may have to go to the forum to attach or download them (rather than by email). ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
I'm wondering something. Have the Rust developers considered the possibility of using references instead of pointers? It seems to me that this would eliminate a lot of the need for autoderef. Now I'm not well-equipped to talk about Rust (some of the rules I am totally ignorant about, e.g. I know rust has a ref keyword but the tutorial does not mention it) so let me talk about C++ instead. C++, of course, has references, which are exactly like pointers except they are always implicitly dereferenced: int y = 123; int* x1 = y; // *x1 == y x1 == y int x2 = y; // x2 == y x2 == y In fact, with one small tweak, C++ could have entirely eliminated the need for pointers (except array pointers), and references could have been used for everything except arrays. The reason references cannot replace pointers is that the pointer--the true identity of a reference--is immutable. But that's easily fixable though, by defining x2 as an lvalue: x1 = z; x2 = z; // Aha! We've changed the pointer inside the reference If the second line were legal, then references could do everything that pointers could do; in that case I think that, for consistency, people would have eventually standardized on using references for everything that doesn't require pointer arithmetic (i.e. arrays). Instead we have this situation where some functions take (non-const) T and others take T*, so sometimes you have to call functions with foo and other times it's just foo, and the presence or absence of doesn't signify anything important (for example, it doesn't tell you whether the callee mutates foo or not) It seems to me that the majority of languages have recognized that references are easier to work with than pointers, and not just because you can write foo.f() instead of foo-f(). That is, I don't think auto-deref (where foo.f() means (*foo).f()) is enough to eliminate the pain of working with pointers instead of references. Would it help to define ~T, T and @T as reference types rather than pointer types? I'm sure there are some obvious objections, but perhaps the problems could be ironed out by someone with a better grasp of Rust. I'd also like to comment on the removal of move. While I recognize explicit moves everywhere might be inconvenient, I have noticed that newbies on the list (including myself) are often confused by them. Would a compromise be in order, in which moves are implicit except when passing parameters to methods? I like Bill Byers' suggestion for call-site parameters of the form move x, mut x and just x for implicit copy / borrow. Patrick mentioned the annoyance of writing let move x = move foo(move y); but let x = foo(move y); doesn't sound so onerous, and it says something important about foo() that someone reading the code might not realize. Everyone's had their fair share of issues with autoref and autoderef, and it's worth considering removing certain portions of it from the compiler. The discussion around this has been rooted in the past, but has recently been brought up as part of https://github.com/mozilla/rust/issues/10504. The current proposal is to remove all autoref except for function invocations and indexing operations. The method of creating T from ~T would be `let foo: T = foo` or `*foo`. Vectors and strings can't currently benefit from the `*foo` syntax, but they will hopefully be able to do such once DST lands. In the meantime coercion via type ascription will work and they also have `as_slice` methods. There are a few reasons backing this proposal: 1. It's inconsistent to have magical autoref in some places, but not in other places. 2. The camp of less compiler magic is better can fly their flag over this change. 3. Code readability does not necessarily benefit from autoref on arguments: let a = ~Foo; foo(a); // reading this code looks like it moves `a` fn foo(_: Foo) {} // ah, nevermind, it doesn't move `a`! let mut a = ~[ ... ]; sort(a); // not only does this not move `a`, but it mutates it! ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Type system thoughts
Personally I'd appreciate a type system that's able to express SI units, which C++ and Haskell are powerful enough to do[1]. I agree. This is of huge importance when it comes to providing compile time safety guarantees. And if the language is powerful enough to express SI units, then it also demonstrates that it might be powerful enough to allow users to grow the language in other directions not anticipated by the language designers. While I'm no type system expert, I implemented a unit inference system for boo (though boo's developers weren't interested and did not use it). So I thought I'd just share some thoughts about how I view types and unit checking (as someone whose university education included absolutely no type theory). I've started to view types as three partly or wholly independent elements: 1. Physical structure. Under this view, two structs with members x: int, y: int can be considered the same structure, and while direct assignment between them may not be allowed, the compiler can safely treat them as the same type IF the language allows it to. 2. Name and attributes. This encompasses things like names and modules where structures live (e.g. a Point structure defined in two different namespaces), const/non-const in C++, mutable-immutable in D, and units (km/L, bytes/sec). Unlike a typing error in (1), a typing error or cast that changes these attributes does not compromise the memory safety of the language. 3. Operations. Often a type has multiple interfaces, e.g. in Java, an object can be accessed through several different interfaces without changing its type. I've been designing a language where this is extended to allow arbitrary user-defined views on a type (a concept which might be very useful in my Loyc project, whose goal is to allow cross-language programming; often there are some types in two different languages that are very similar and just need someone to define a mapping between them.) I wonder if anyone has formalized a view like this before. Anyway, viewing (2) as a plurality of attributes, independent from (1), might allow one to develop a type system where user-defined attribute typing is straightforwardly possible. A simple version of this would be to allow users to install a plug-in shared library that the compiler uses for additional analysis following standard typing. Then unit inference could be installed as one such library. These libraries would not necessarily be allowed to change the behavior of the program; it would be useful enough just to allow them to perform analysis, but it would be more useful if they could alter behavior too (e.g. the unit-checking module could automatically coerce km to mi or m or yd, adding a scaling factor; or in a language with overloading, functions could be overloaded based on unit types.) Note that typing failure in (2) need not prevent the code from being compiled (in general I like a language that doesn't force me to fix every little problem before running the code.) A general facility for annotating code is necessary to allow extra typing without changing the language grammar (e.g. in my languages LES and EC# my expression grammar allows attributes in [SquareBracks] at the beginning of any expression or subexpression in parentheses, plus I support some user-defined operators.) One thing I want to say about unit checking, it's much more useful to provide unit inference rather than unit checking, because users don't want to write annotations all over the place. A function like fn abs(x: int) - int { if x 0 { -x } else { x } } should need no annotation for the unit inferer to understand that 'x' has polymorphic units, and in fn length(self) { sqrt(self.x * self.x, self.y * self.y) } 'x' and 'y' can be constrained automatically to have the same units. (there are a bunch of subtle details of course, but since I wrote the inference engine about 7 years ago, I forgot many of them...) -- - David http://loyc-etc.blogspot.com ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Abandoning segmented stacks in Rust
Segmented stacks aren't the only solution though. If the concern is many tasks that block for a long time, I imagine a mechanism to bundle a bunch of small, dormant stacks into a single page so that the original pages could be released to the OS. If stacks were additionally relocatable (which requires similar machinery as precise moving GC, if I'm not mistaken) then the released pages could be re-used for other tasks or heaps, which would be especially helpful on 32-bit. To address the problem of running out of address space on 32-bit, small-stack tasks could request a single-page stack (plus a guard page), which is enlarged by relocation as needed. Combining these techniques, you might be able to have up to a million small tasks in a 32-bit process. From: Bill Myers bill_my...@outlook.com The advantage of segmented stacks is that blocked tasks only take up as much memory as they actually need to store state, so that for instance a network server can use a task for each connection, and still only use, say, 64 bytes per connection if that's possible instead of the number of stack pages that got allocated for previous computation (assuming an extreme version that allocates a stack segment on every call). However, there is another approach that can replace segmented stacks for that purpose, namely having the compiler automatically transform blocking functions to instead return a future (with limited lifetime). This means that segmented allocation only happens for functions that indirectly perform I/O and only allocates the exact amount of memory needed to retain state that must persistent across the blocking I/O operation, while other functions execute normally using traditional stacks. The simplest example of this feature is async/await in C# 5, and Scala has a delimited continuation passing transformation that can be used to do the same thing. Has this been considered for Rust? ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mozilla hiring a research engineer to work on Rust
Mozilla is going to hire another engineer to work on Rust! As we head toward Rust 1.0 we are looking for a motivated individual with serious chops to help us grind through the remaining blocking bugs. Enthusiasm for Servo will also be looked upon with great favor. See all the gory details here: https://careers.mozilla.org/en-US/position/oMiEXfwp I was a bit shocked to receive this rejection after just 2 hours and 15 minutes: Thank you for taking the time to apply at Mozilla. We are always looking for help creating great software and appreciate your offer to work with us! Unfortunately, we're not always able to find a match within our organization for everyone's skills and experience. After reviewing your application we did not find a fit at this time, and as a result will not be able to move forward further. If you don't want to give a reason, that's you're perogative, I guess, but this message doesn't even acknowledge that I applied for a *specific* position. I don't believe in 2 hours and 15 minutes someone actually checked if I was eligible for each of the available openings, not that I'm interested in most of the other positions anyhow. Also, I would at least like to know that the Rust team itself rejected me and not some generic HR person. Also, if you are looking for ways to help build a better internet, there is no easier way than running Firefox Aurora as your daily browser and filing bugs and/or sending us your feedback. Getting hired by Mozilla could have changed my life. So this is a bit of a tacky way to finish the rejection, don't you think? ___ 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
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)`. I see no reason for the restriction of self. Why not simply say that any function can be called with first_arg.func(...) style ? D has that rule (which they call UFCS), and I find it confusing sometimes. When a function is declared with function syntax and it makes sense or feels right that it should be a function, my mind is not expecting to see method syntax and I may have trouble figuring out later what an apparent method call refers to, even if I've seen the function declaration. Mind you it is worse in D than Rust, because in D, function declarations can be hidden inside what they call eponymous templates, which has stumped me in the past. In any case, wouldn't it help narrow down searches if you could search for fn foo(self instead of only fn foo? (again though, Rust is not as bad for searching as the C-family languages that have no fn keyword.) In C# it's nice that I can find all the so-called extension methods by searching for (this . ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] real-time programming? usability?
* Use postfix syntax for pointer dereference, like in Pascal: (~rect).area() becomes rect~.area() . That reads left-to-right with nary a precedence mistake. While Rust?s auto-dereference feature and type checker will sometimes catch that mistake, it's better to just fix the failure mode. All security holes in the field got past the type checker and unit tests. Do you realise that `~rect` means create an owned box [a pointer] that contains `rect` and isn't not a dereference in Rust? (That is performed by `*rect`.) I would add that if pointer dereference were a suffix operator, it would have to be changed from * to (e.g.) ~. Why? Because if foo* means dereference foo then it becomes ambiguous whether foo * - bar means (foo*)-bar dereference foo and subtract bar or foo*(-bar) multiply foo by negated bar. Due to this ambiguity, it is difficult for a language to simultaneously have 1. Operators that can be prefix or infix, and 2. Operators that can be infix or suffix. To avoid the ambiguity, one can introduce an operator like ~ that is prefix or suffix but never infix. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Structural enums for datasort refinements
On Wed, Aug 28, 2013 at 11:19 AM, David Piepgrass qwertie...@gmail.comwrote: From: Bill Myers bill_my...@outlook.com I was reading a proposal about adding datasort refinements to make enum variants first-class types, and it seems to me there is a simpler and more effective way of solving the problem. The idea is that if A, B and C are types, then A | B | C is a structural enum type that can be either A, B or C. ... Hi, I'm just your friendly everyday lurker... Assuming there's nothing in this proposal that fundamentally breaks the language, I just wanted to say I think this idea is fantastic. You almost don't need Some(T) since OptionT could just be None|T. I would add that I recall recently some people were discussing how to do a variant type in Rust. If all struct types in a program are assigned a unique tag, this very same feature could support a variant type, which is defined as the union of all types. Then any struct converts implicitly to variant (oh, and since I just wrote a parser generator that supports inverted sets like ~('a'..'z'), it occurs to me that the type system could readily support any type except A | B as well.) The tag could either be a unique integer index, or a pointer to a global variable that contains type information (the latter, it seems to me, automatically solves the dynamic linking problem how do we ensure two unrelated DLLs have unique tags? DLL loaders already support relocation in case of conflicts.) Oops, I just remembered that Rust implements enum types using a fixed-size memory area that is as large as the largest possible item. So a variant (or anything except) type, having unlimited size, doesn't directly fit into that model. But the compiler could switch to a heap representation for variants. Or, better, variants could use a smart fixed-size representation in which heap allocation is used only when the variant is storing a larger type. For instance, a variant could always use two words, one word for the type tag and one word for the value. If the current data type T is one word or smaller, it is stored directly in the second word. Otherwise, the second word is a ~T pointer. Just an idea. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Structural enums for datasort refinements
From: Bill Myers bill_my...@outlook.com I was reading a proposal about adding datasort refinements to make enum variants first-class types, and it seems to me there is a simpler and more effective way of solving the problem. The idea is that if A, B and C are types, then A | B | C is a structural enum type that can be either A, B or C. In addition, A can be implicitly converted to A | B, A | B can be implicitly converted to A | B | C, and also (A | B) | C and A | (B | C) are equivalent to A | B | C, and finally C | B | A is equivalent to A | B | C (to support the latter, the implementation needs to sort variants in some arbitrary total order before assigning tag numbers). Furthermore, a way to bind variables to an or pattern is introduced to allow to convert A | B | C to A | B in the case that it holds an A or a B. This way, one can rewrite Option as a type alias like this: struct SomeT(T); struct None; type OptionT = None | SomeT; Which is like the current Option, but also makes None and SomeT first-class types. The current enum syntax can remain as syntax sugar for the above code. Hi, I'm just your friendly everyday lurker... Assuming there's nothing in this proposal that fundamentally breaks the language, I just wanted to say I think this idea is fantastic. You almost don't need Some(T) since OptionT could just be None|T. I would add that I recall recently some people were discussing how to do a variant type in Rust. If all struct types in a program are assigned a unique tag, this very same feature could support a variant type, which is defined as the union of all types. Then any struct converts implicitly to variant (oh, and since I just wrote a parser generator that supports inverted sets like ~('a'..'z'), it occurs to me that the type system could readily support any type except A | B as well.) The tag could either be a unique integer index, or a pointer to a global variable that contains type information (the latter, it seems to me, automatically solves the dynamic linking problem how do we ensure two unrelated DLLs have unique tags? DLL loaders already support relocation in case of conflicts.) The only issue I see is what to do for code such as let mut x = Some(3); x = None;: with this proposal, Some and None are separate unrelated types, so we either have this code emit an error, or x must be given the type Someint | None automatically, which however can lead to obscure error messages if one mistakenly attempts to assign a string to it causing the type to become Someint | None | ~str (i.e. the user might be told than a match is not exhaustive because it does not handle the ~str case, rather than that they assigned a ~str to an Option-typed variable). It should be possible to allow this, and make the error-emitting code use heuristics to figure out whether it is more likely that the user assigned a value of the wrong type, or used an enum improperly (for example, by looking at whether the implicitly created enum type is ever written explicitly in the source, and whether the deduced structural enum type is being used in places that require a non-enum type). Alternatively, one can stipulate that only types that are structs, or that are structs marked enum struct or case struct or similar can become part of an inferred structural enum, but this seems unappealing. To keep the feature more conservative it's not unreasonable to (initially, at least) restrict the members of the union to be struct types; any non-struct type can still be used with Some(T). But the restriction doesn't entirely solve this particular problem; yeah, the compiler will complain if you assign ~str to a variable intended to be an Option, but it still won't complain if you assign some struct type to it. Note that some structural enums can change representations depending generic instantiation, since T | int becomes just int if T = int, while it is ~str | int if T = ~str (and similar for SomeT | Someint), but this should not be a problem. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
[rust-dev] Can Rust allow a bitwise equality test?
I think at the least we should offer a #[deriving(Basics)] for use on public types so that people aren't forced to memorize Eq Ord TotalOrd TotalEq IterBytes Clone (unless we can find a silly SFINAE-esque acronym... http://www.wordsmith.org/anagram/anagram.cgi?anagram=eottic ). Plenty of types can't actually be ordered, and in *many* cases not all fields should be considered for equality/ordering and they may or may not be listed in the order the comparison should try. The only two that rarely require any extra consideration are `Clone`/`DeepClone`, since they should be on almost every type without a destructor. I just had a random thought. I once implemented a data structure for .NET called a VList ( http://www.codeproject.com/Articles/26171/VList-data-structures-in-C) which provided a SmartSelect pseudo-LINQ method. If you had a list of numbers, you could ensure they are all positive like this: list.SmartSelect(x = Math.Max(x, 1)) The reason it's called SmartSelect is that it returns a changed list only if the select operation *changes* any of the values (and even then it may still be able to re-use the tail of the list). The tricky part is detecting when there has been any changes. Ideally I would have liked to simply do a bitwise equality test because it would be very fast and requires no special support from the data type, but .NET doesn't have a bitwise equality test. So I ended up having to use a test that always requires dynamic method invocation, which I have heard is fantastically slow in the general case of structs that don't implement IEquatableT. I wonder if Rust could, in the general case, allow my bitwise equality test. I realize that bitwise equality isn't real equality, but in this case it's good enough. For instance if the selector changes -0.0 to +0.0, which is value-equal but bitwise-different, the effect is fairly harmless--a partially new list is allocated instead of re-using the old list. Of course, if the list contains references (~T, @T), the bitwise test should compare the references themselves, without dereferencing. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] rustdoc rewrite and redesign
It might be worth looking into how .NET or other platforms with similar architecture accomplish this, if at all. The C# compiler has a switch that converts C# XML doc comments into an XML file, which is placed in the output folder beside the .DLL or .EXE file. This starting point makes a lot of sense, as binaries can be distributed with their documentation, and IDE intellisense can show basic documentation directly from the XML file, which is found automatically by looking for an XML file beside each referenced DLL. (Creating pretty-printed HTML documentation from the XML gets a bit complicated; a remarkably large number of separate tools are involved, but these tools are bundled together, and thanks to IDE features, the plain XML gets a lot of mileage.) While creating XML, the compiler resolves short references like see cref=Foo/ into a complete reference like see cref=M:Blah.Blah.Foo/ (where M means Method, T means Type, etc.) -- - David http://loyc-etc.blogspot.com ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
[rust-dev] bikeshed on closure syntax
This requires arbitrary lookahead to disambiguate from tuples. This bit in particular. Really really don't want to cross the bridge to arbitrary lookahead in the grammar. Pardon me, but I'm not convinced that there is a problem in lambdas like (x, y) - (x + y). By analogy, you can realize that ((x * y) + z, q) is a tuple instead of a simple parenthesized expression when you reach the comma -- you don't need to look ahead for a comma in advance. So why not treat (x, y) as a tuple until you reach the - and then reinterpret the contents at that point? This works as long as the syntax of a lambda argument list is a subset of the tuple syntax, anyway. If that's not the case, parsing gets messier, though I'm sure arbitrary lookahead is not be the only possible implementation. -- - David http://loyc-etc.blogspot.com ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev