Re: [rust-dev] Proposal: Change Parametric Polymorphism Declaration Syntax
On Sun, 2014-02-02 at 14:45 +1300, Nick Cameron wrote: - The change adds boilerplate and nomenclature that is likely unfamiliar to our target audience - 'for all' is well known to functional programmers, but I believe that is not true for most users of C++ (or Java). Being closer to the C++/Java syntax for generics is probably more 'intuitive So instead of 'for all', use 'template', and we're closer to C++ syntax than ever! templateT: typename, U: typename struct Foo { ... } templateT: typename, U: typename impl TraitT for FooT, U { ... } templateT: typename, U: typename fn foo(...) { ... } ;-) fwiw, like C++, Java generic methods also put the type parameter list in front of the function signature rather than behind the function name: public static T extends ComparableT int countGreaterThan(T[] anArray, T elem) { ... } ... while C# apparently compromises and puts the type parameters between the function name and value parameter list, but leaves the bounds for later: public static bool ContainsT(IEnumerableT collection, T item) where T : IComparableT; Neither approach translates too well into Rust, but that Rust is almost the odd one out here makes me sympathetic to the desire to avoid breaking up particularly function declarations between the name and the value parameter list too much, in spite of the familiarity argument. Of course, like everything else, that has to be balanced with avoiding superficial but far-reaching overhauls of the language at the eleventh hour. Alas! -benh ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Proposal: Change Parametric Polymorphism Declaration Syntax
On 02/01/2014 11:39 PM, Corey Richardson wrote: ``` forallT, U struct Foo { ... } forallT, U impl TraitT for FooT, U { ... } forallT, U fn foo(...) { ... } ``` Why not ``` fn foo: pub unsafe T, U = (f: |T| - U, arg: T) - U { f(arg) } struct foo: T, U = { ... } impl Foo: T, U = TraitT { ... } ``` Can we please not put more stuff in front of the identifier? Have we ran out of space after it? What will this look like once the types start looking like `templatetypename KeyFromValue, typename Hash, typename Pred, typename SuperMeta,typename TagList, typename Category class hashed_index`? Do we really need all that in front of a function name? The immediate, and most pragmatic, problem is that in today's Rust one cannot easily search for implementations of a trait. Why? Because the only existing Rust parser is geared towards a native Rust compiler, not a non-Rust IDE. That's the problem, not `grep`, and syntax changes won't help unless you want to redesign the language to be completely regex-compatible. Write a C-bindable parser, plug it into `ack`, `ctags` and a couple of IDEs, and everyone will be happy. (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.) Then why have such a parsing-oriented grammar in the first place? Following this logic, Rust should look more like Haskell, with a Hello Kitty binop `(=^.^=)`. (I swear I've seen this in real code somewhere) ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Proposal: Change Parametric Polymorphism Declaration Syntax
I'm not a huge fan of this proposal. It makes declarations longer, and it removes the visual consistency of FooT,U everywhere, which I think introduces its own pedagogical issue. The recent addition of default type parameters, though, makes me think there's a reasonable change that increases consistency and shortens declarations in a few common cases. From what I understand, the reason we can't just have impl TraitT for FooT,U is because it's ambiguous whether T and U are intended to be concrete or generic type names; i.e., implT TraitT for FooT,U tells the compiler that we expect U to be a concrete type name. Our new default type parameter declarations look like: struct FooT,U=Bar So what if to actually make generic types concrete, we always used the '='? struct FooT,U=Bar impl TraitT for FooT, U=Derp This saves a character over 'implT TraitT for FooT, Derp', solves the greppability problem, and makes intuitive sense given how defaults are declared. It also has a nice parallel with how ':' is used - ':' adds restrictions, '=' fully locks in place. So what is today something like implT:Ord TraitT for FooT, Derp would become impl TraitT:Ord for FooT, U=Derp The rule would be that the first use of a type variable T would introduce its bounds, so for instance: impl TraitT:Ord for FooZ:Clone, U=Derp would be fine, and impl TraitT for FooT:Clone, U=Derp would be an error. More nice fallout: struct FooA,B impl FooA,B=Bar { fn one(a: A) - B fn two(a: A) - B fn three(a: A) - B } means that if I ever want to go back and change the name of Bar, I only have to do it in one place, or if Bar is actually some complicated type, I only had to write it once, like a little local typedef. I'm sure this has some glaring obvious flaw I'm not thinking of. It would be nice to have less syntax for these declarations, but honestly I'm ok with how it is now. On Sat, Feb 1, 2014 at 5:39 PM, Corey Richardson co...@octayn.net wrote: 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 FooT, U { ... } implT, U TraitT for FooT, U { ... } fn fooT, U(...) { ... } ``` to: ``` forallT, U struct Foo { ... } forallT, U impl TraitT for FooT, U { ... } forallT, U 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.) A deeper, more pedagogical problem, is the mismatch between how `struct Foo... { ... }` is read and how it is actually treated. The straightforward, left-to-right reading says There is a struct Foo which, given the types ... has the members This might lead one to believe that `Foo` is a single type, but it is not. `Fooint` (that is, type `Foo` instantiated with type `int`) is not the same type as `Foounit` (that is, type `Foo` instantiated with type `uint`). Of course, with a small amount of experience or a very simple explanation, that becomes obvious. Something less obvious is the treatment of functions. What does `fn foo...(...) { ... }` say? There is a function foo which, given types ... and arguments ..., does the following computation: ... is not very adequate. It leads one to believe there is a *single* function `foo`, whereas there is actually a single `foo` for every substitution of type parameters! This also holds for implementations (both of traits and of inherent methods). Another minor problem is that nicely formatting long lists of type parameters or type parameters with many bounds is difficult. Proposed Solution = Introduce a new keyword, `forall`. This choice of keyword reads very well and will not conflict with any identifiers in code which follows the [style guide](https://github.com/mozilla/rust/wiki/Note-style-guide). Change the following declarations from ``` struct FooT, U { ... } implT, U TraitT for FooT, U { ... } fn fooT, U(...) { ... } ``` to: ``` forallT, U struct Foo { ... } forallT, U impl TraitT for FooT, U { ... } forallT, U fn foo(...) { ... } ``` These read very well. for all types T and U, there is a struct Foo ..., for all types T and U, there is a function foo ..., etc. These reflect that there are in fact multiple functions `foo` and structs `Foo` and implementations of `Trait`, due to monomorphization. [0]:
Re: [rust-dev] Proposal: Change Parametric Polymorphism Declaration Syntax
After sleeping on it I'm not convinced that this would be a net improvement over our current situation. With a few caveats I'm really rather happy with the syntax as it is. On Sun, Feb 2, 2014 at 8:55 AM, Jason Fager jfa...@gmail.com wrote: I'm not a huge fan of this proposal. It makes declarations longer, and it removes the visual consistency of FooT,U everywhere, which I think introduces its own pedagogical issue. The recent addition of default type parameters, though, makes me think there's a reasonable change that increases consistency and shortens declarations in a few common cases. From what I understand, the reason we can't just have impl TraitT for FooT,U is because it's ambiguous whether T and U are intended to be concrete or generic type names; i.e., implT TraitT for FooT,U tells the compiler that we expect U to be a concrete type name. Our new default type parameter declarations look like: struct FooT,U=Bar So what if to actually make generic types concrete, we always used the '='? struct FooT,U=Bar impl TraitT for FooT, U=Derp This saves a character over 'implT TraitT for FooT, Derp', solves the greppability problem, and makes intuitive sense given how defaults are declared. It also has a nice parallel with how ':' is used - ':' adds restrictions, '=' fully locks in place. So what is today something like implT:Ord TraitT for FooT, Derp would become impl TraitT:Ord for FooT, U=Derp The rule would be that the first use of a type variable T would introduce its bounds, so for instance: impl TraitT:Ord for FooZ:Clone, U=Derp would be fine, and impl TraitT for FooT:Clone, U=Derp would be an error. More nice fallout: struct FooA,B impl FooA,B=Bar { fn one(a: A) - B fn two(a: A) - B fn three(a: A) - B } means that if I ever want to go back and change the name of Bar, I only have to do it in one place, or if Bar is actually some complicated type, I only had to write it once, like a little local typedef. I'm sure this has some glaring obvious flaw I'm not thinking of. It would be nice to have less syntax for these declarations, but honestly I'm ok with how it is now. On Sat, Feb 1, 2014 at 5:39 PM, Corey Richardson co...@octayn.net wrote: 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 FooT, U { ... } implT, U TraitT for FooT, U { ... } fn fooT, U(...) { ... } ``` to: ``` forallT, U struct Foo { ... } forallT, U impl TraitT for FooT, U { ... } forallT, U 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.) A deeper, more pedagogical problem, is the mismatch between how `struct Foo... { ... }` is read and how it is actually treated. The straightforward, left-to-right reading says There is a struct Foo which, given the types ... has the members This might lead one to believe that `Foo` is a single type, but it is not. `Fooint` (that is, type `Foo` instantiated with type `int`) is not the same type as `Foounit` (that is, type `Foo` instantiated with type `uint`). Of course, with a small amount of experience or a very simple explanation, that becomes obvious. Something less obvious is the treatment of functions. What does `fn foo...(...) { ... }` say? There is a function foo which, given types ... and arguments ..., does the following computation: ... is not very adequate. It leads one to believe there is a *single* function `foo`, whereas there is actually a single `foo` for every substitution of type parameters! This also holds for implementations (both of traits and of inherent methods). Another minor problem is that nicely formatting long lists of type parameters or type parameters with many bounds is difficult. Proposed Solution = Introduce a new keyword, `forall`. This choice of keyword reads very well and will not conflict with any identifiers in code which follows the [style guide](https://github.com/mozilla/rust/wiki/Note-style-guide). Change the following declarations from ``` struct FooT, U { ... } implT, U TraitT for FooT, U { ... } fn fooT, U(...) { ... } ``` to: ``` forallT, U struct Foo { ... } forallT, U impl TraitT for FooT, U { ... } forallT, U fn foo(...) { ... } ``` These read very
Re: [rust-dev] Proposal: Change Parametric Polymorphism Declaration Syntax
On Sun, Feb 2, 2014 at 6:08 PM, Benjamin Striegel ben.strie...@gmail.comwrote: After sleeping on it I'm not convinced that this would be a net improvement over our current situation. With a few caveats I'm really rather happy with the syntax as it is. On Sun, Feb 2, 2014 at 8:55 AM, Jason Fager jfa...@gmail.com wrote: I'm not a huge fan of this proposal. It makes declarations longer, and it removes the visual consistency of FooT,U everywhere, which I think introduces its own pedagogical issue. The recent addition of default type parameters, though, makes me think there's a reasonable change that increases consistency and shortens declarations in a few common cases. From what I understand, the reason we can't just have impl TraitT for FooT,U is because it's ambiguous whether T and U are intended to be concrete or generic type names; i.e., implT TraitT for FooT,U tells the compiler that we expect U to be a concrete type name. Our new default type parameter declarations look like: struct FooT,U=Bar So what if to actually make generic types concrete, we always used the '='? struct FooT,U=Bar impl TraitT for FooT, U=Derp This saves a character over 'implT TraitT for FooT, Derp', solves the greppability problem, and makes intuitive sense given how defaults are declared. It also has a nice parallel with how ':' is used - ':' adds restrictions, '=' fully locks in place. So what is today something like implT:Ord TraitT for FooT, Derp would become impl TraitT:Ord for FooT, U=Derp The rule would be that the first use of a type variable T would introduce its bounds, so for instance: impl TraitT:Ord for FooZ:Clone, U=Derp would be fine, and impl TraitT for FooT:Clone, U=Derp would be an error. More nice fallout: struct FooA,B impl FooA,B=Bar { fn one(a: A) - B fn two(a: A) - B fn three(a: A) - B } means that if I ever want to go back and change the name of Bar, I only have to do it in one place, or if Bar is actually some complicated type, I only had to write it once, like a little local typedef. I'm sure this has some glaring obvious flaw I'm not thinking of. It would be nice to have less syntax for these declarations, but honestly I'm ok with how it is now. On Sat, Feb 1, 2014 at 5:39 PM, Corey Richardson co...@octayn.netwrote: 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 FooT, U { ... } implT, U TraitT for FooT, U { ... } fn fooT, U(...) { ... } ``` to: ``` forallT, U struct Foo { ... } forallT, U impl TraitT for FooT, U { ... } forallT, U fn foo(...) { ... } ``` From a readability point of view, I am afraid this might be awkward though. Coming from a C++, I have welcome the switch from `typedef` to `using` (aliases) because of alignment issues; consider: typedef std::mapint, std::string MapType; typedef std::vectorstd::pairint, std::string VectorType; vs using MapType = std::mapint, std::string; using VectorType = std::vectorstd::pairint, std::string; In the latter, the entities being declared are at a constant offset from the left-hand margin; and close too; whereas in the former, the eyes are strained as they keep looking for what is declared. And now, let's look at your proposal: fn foo(a: int, b: int) - int { } fn fooT, U(a: T, b: U) - T { } forallT, U fn foo(a: T, b: U) - T { } See how forall causes a bump that forces you to start looking where that name is ? It was so smooth until then ! So, it might be a net win in terms of grep-ability, but to be honest it seems LESS readable to me. -- Matthieu 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.) A deeper, more pedagogical problem, is the mismatch between how `struct Foo... { ... }` is read and how it is actually treated. The straightforward, left-to-right reading says There is a struct Foo which, given the types ... has the members This might lead one to believe that `Foo` is a single type, but it is not. `Fooint` (that is, type `Foo` instantiated with type `int`) is not the same type as `Foounit` (that is, type `Foo` instantiated with type `uint`). Of course, with a small amount of experience or a very simple explanation, that becomes obvious. Something less obvious is the treatment of functions. What
Re: [rust-dev] Proposal: Change Parametric Polymorphism Declaration Syntax
Also after sleeping on it I'm not as big of a fan of this proposal. But, I find the idea raised earlier of having generic blocks to group implementations etc that have the same implementation nice. Fully backwards compat though, so I'm not going to worry about it. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Deprecating rustpkg
[ I don't want to start another argument, but since you guys are discussing 0install, maybe I can provide some useful input... ] On 2014-02-02 07:20, Vladimir Matveev wrote: How will it handle external dependencies? I don't think it should. External dependencies are way too complex. They come in different flavors on different systems. On Windows, for example, you don't have a package manager, and you'll have to ship these dependencies with the program using an installer. On each Linux distro there is custom package manager, each having its own strategy of naming things and its own versioning policy. It is impossible to unify them, and I don't think that Rust package manager should attempt to do this. I don't understand this. A package manager specific to Rust is additional software, just like 0install. 0install has full support for installing dependencies via the system package manager on many systems if desired. *End users* won't need Rust package manager at all (unless they want to install development versions of Rust software). Only package maintainers and developers have to use it. End users just use their native package manager to obtain packages created by maintainers. If Rust would depend on zero install, however, end user will be *forced* to use zero install. I don't follow this. Whether the developer uses 0install to get the build dependencies doesn't make any difference to the generated binary. Of course, you *can* distribute the binary using 0install too, but you're not required to. I'm against of using zero install for the following reasons. First, it is just a packaging system. It is not supposed to help in building Rust software. But resolving build dependencies and invoking the compiler with correct paths to installed dependencies is crucial. How, for example, zero install would handle dependency to master branch of some source repository? 0install doesn't automatically check out Git repositories (although that would be handy). Here's how we currently do it: - Your program depends on libfoo = 1.0-post - The latest released version of libfoo is only 1.0 - You git clone the libfoo repository yourself and register the metadata (feed) file inside it: $ git clone git://.../libfoo $ 0install add-feed libfoo/feed.xml - 0install now sees that libfoo 1.0 and 1.0-post are both available. Since your program requires libfoo = 1.0-post, it will select the Git checkout version. What if I'm developing several packages which depend on different versions of the same package? Zero install allows installing multiple versions of the same package, yes, but how should I specify where these libraries are located to the compiler? Given a set of requirements, 0install will tell you where some suitable versions of the dependencies are. For example: $ cd /tmp $ git clone https://github.com/0install/hello-scons.git $ cd hello-scons $ 0install download Hello-scons.xml --source --show - URI: /tmp/hello-scons/Hello-scons.xml Version: 1.1-post Path: /tmp/hello-scons - URI: http://0install.net/2006/3rd-party/SCons.xml Version: 2.0.1 Path: /var/cache/0install.net/implementations/sha1new=86311df9d410de36d75bc51762d2927f2f045ebf - URI: http://repo.roscidus.com/python/python Version: 2.7.6-1 Path: (package:arch:python2:2.7.6-1:x86_64) This says that the build dependencies are: - This package's source code (in /tmp/hello-scons) - The SCons build tool (which 0install has placed in /var/cache) - Python (provided by the distribution) The source could also specify library dependencies. How do you get this information to the build tool? The usual way is to tell 0install how to run the build tool in the XML. In this case, by running SCons on the project's SConstruct file. But you could get the information to it some other way. For example, a rustpkg tool that invokes 0install download ... --source --xml behind the scenes and does something with the machine-readable selections document produced. How should I specify build dependencies for people who want to hack on my package? List them in the XML file that is in your project's source repository. Users should then be able to clone your git repository and build, with build dependencies handled for them. Majority of direct dependencies will be from the Rust world, and dedicated building/packaging tool would be able to download and build them automatically as a part of build process, and only external dependencies would have to be installed manually. With zero install you will have to install everything, including Rust-world dependencies, by yourself. 0install should be able to handle all build dependencies (e.g. libraries, the Rust compiler, build tools, documentation tools, etc). Second, it is another package manager which is foreign to the system (unless the system uses zero install as its package manager, but I think only very minor
Re: [rust-dev] Deprecating rustpkg
A general observation (not particularly replying to your post, Thomas). For both python and haskell (just to name two languages), distribution (where things end up on the filesystem ready to be used) can be done by both the built-in tools (cabal-install, pip) and the distribution-specific tools. Gentoo even has a tool to take a cabal package and generate an ebuild from it - https://github.com/gentoo-haskell/hackport. In the Haskell world cabal and cabal-install are separated ( http://ivanmiljenovic.wordpress.com/2010/03/15/repeat-after-me-cabal-is-not-a-package-manager/) - which is probably a good thing and maybe something we can consider for rustpkg. (The link btw is quite interesting in its own right and perhaps some more inspiration could be taken from there). My point is that a building tool should be able to either fetch or look up dependencies that already exist in a well-specified layout on the filesystem (for the case of development and production deployment using a distro package manager respectively). Whether that is a single tool or two tools is a point of design; I think both are necessary. I feel there is enough that's been discussed on this thread for a write-up on a wiki or the beginnings of a design/goals document that can later be presented for another discussion. I don't see an existing place on the wiki for this though - where should it go? On Sun, Feb 2, 2014 at 7:47 PM, Thomas Leonard tal...@gmail.com wrote: [ I don't want to start another argument, but since you guys are discussing 0install, maybe I can provide some useful input... ] ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
[rust-dev] Using Default Type Parameters
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). I've already come across situations where default typarams will make for a nicer API, but I'm wondering whether I should use them without hesitation, looking forward to when they are no longer gated, or whether I should shun them because they will make my code incompatible with future changes to the language. Is there any thoughts on this so far? This question also applies to other feature gatess; the level of assurance one can use a given feature with without having to deal with explosive breakage down the line. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Using Default Type Parameters
On Mon, Feb 3, 2014 at 7:55 AM, Corey Richardson co...@octayn.net 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