Re: [rust-dev] mutable vs. functional APIs
In my opinion, there are two main reasons why one would prefer an immutable API over a mutable one: aliasing and sharing substructures. Given that unique pointers and references have mostly solved the first one, in my opinion we should prefer mutable APIs unless the API is going to take advantage of structure sharing. I don't think it makes a lot of sense to have immutable APIs like this: fn frob(mut self) { ... } fn frobed(self) - Frob { let x = self.clone(); x.frob(); x } It's a lot more straightforward for the users to write `let y = x.clone(); y.frob();`, and it protects us from another combinatorial explosion of methods. All that being said, I feel there is an open question on whether or not to prefer statement oriented mutable APIs (methods like `fn frob(mut self)`) and chain-oriented APIs (methods like `fn frob(self) - Frob`). Does anyone have a good argument for one over the other? On Sat, Oct 19, 2013 at 9:42 AM, Eric Sampson eric.samp...@gmail.comwrote: Date: Fri, 18 Oct 2013 10:54:23 -0700 From: Jeff Petkau j...@google.com On my code (disclaimer: only toy projects to learn Rust so far), I've been pretty happy with a mut_ prefix for mutable versions. newthing = oldthing.push(foo) anything.mut_push(foo) x = bagofun.sort() bagosadness.mut_sort() etc. Advantages: - consistent meaning with the 'mut' keyword. - works with pretty much any name. - makes mutable versions just a bit uglier than immutable code. Disadvantages: - If an API is inherently mutable, it gets pretty noisy. - A bit ugly, probably turns off newcomers. - If the compiler warns on ignoring return values, and mutating methods return (), then a convention might be unnecessary. --Jeff What about establishing a convention that mutable methods start with an uppercase letter while non-mutating methods start with a lowercase letter? It would be lightweight in terms of character count/looks and at the same time give mutable methods a slight visual emphasis, which makes sense I think. I know this convention is already used by Traits, but when I looked through some code with the above proposal in mind it would be easy to distinguish between these two uses of the same convention due to the differing contexts they're used in. -Eric ___ 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] mutable vs. functional APIs
chain-oriented APIs (methods like `fn frob(self) - Frob`) What about: ~~~ fn frob(self) - Frob { let mut x = self;// not sure if you need `cast::transmute_mut` here x.thing = foo(); x } ~~~ That would solve the 'copying' problem. I was actually considering doing this as a way of initialising an object in a fluent style: ~~~ let perlin = Perlin::from_seed_str(Kittens) .with_frequency(2.0) .with_octaves(3); ~~~ ~Brendan On 22/10/2013, at 9:18 AM, Erick Tryzelaar erick.tryzel...@gmail.com wrote: In my opinion, there are two main reasons why one would prefer an immutable API over a mutable one: aliasing and sharing substructures. Given that unique pointers and references have mostly solved the first one, in my opinion we should prefer mutable APIs unless the API is going to take advantage of structure sharing. I don't think it makes a lot of sense to have immutable APIs like this: fn frob(mut self) { ... } fn frobed(self) - Frob { let x = self.clone(); x.frob(); x } It's a lot more straightforward for the users to write `let y = x.clone(); y.frob();`, and it protects us from another combinatorial explosion of methods. All that being said, I feel there is an open question on whether or not to prefer statement oriented mutable APIs (methods like `fn frob(mut self)`) and chain-oriented APIs (methods like `fn frob(self) - Frob`). Does anyone have a good argument for one over the other? On Sat, Oct 19, 2013 at 9:42 AM, Eric Sampson eric.samp...@gmail.com wrote: Date: Fri, 18 Oct 2013 10:54:23 -0700 From: Jeff Petkau j...@google.com On my code (disclaimer: only toy projects to learn Rust so far), I've been pretty happy with a mut_ prefix for mutable versions. newthing = oldthing.push(foo) anything.mut_push(foo) x = bagofun.sort() bagosadness.mut_sort() etc. Advantages: - consistent meaning with the 'mut' keyword. - works with pretty much any name. - makes mutable versions just a bit uglier than immutable code. Disadvantages: - If an API is inherently mutable, it gets pretty noisy. - A bit ugly, probably turns off newcomers. - If the compiler warns on ignoring return values, and mutating methods return (), then a convention might be unnecessary. --Jeff What about establishing a convention that mutable methods start with an uppercase letter while non-mutating methods start with a lowercase letter? It would be lightweight in terms of character count/looks and at the same time give mutable methods a slight visual emphasis, which makes sense I think. I know this convention is already used by Traits, but when I looked through some code with the above proposal in mind it would be easy to distinguish between these two uses of the same convention due to the differing contexts they're used in. -Eric ___ 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] mutable vs. functional APIs
On Mon, Oct 21, 2013 at 4:10 PM, Brendan Zabarauskas bjz...@yahoo.com.auwrote: chain-oriented APIs (methods like `fn frob(self) - Frob`) What about: ~~~ fn frob(self) - Frob { let mut x = self;// not sure if you need `cast::transmute_mut` here x.thing = foo(); x } ~~~ You don't need the `transmute_mut` here, doing `let mut x = self;` works just fine. That would solve the 'copying' problem. I was actually considering doing this as a way of initialising an object in a fluent style: ~~~ let perlin = Perlin::from_seed_str(Kittens) .with_frequency(2.0) .with_octaves(3); ~~~ I agree, it's great for initialization, I've used this approach too for https://github.com/erickt/rust-elasticsearch for building up JSON objects. I could see some logic to us using chaining by default. It has some nice symmetry with the iterator protocol: ``` fn frob(xs: ~[int]) - ~[int] { xs .push(0) .push(1) .push(2) .move_iter().map(|x| x + 1).collect() } ``` ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] mutable vs. functional APIs
Date: Fri, 18 Oct 2013 10:54:23 -0700 From: Jeff Petkau j...@google.com On my code (disclaimer: only toy projects to learn Rust so far), I've been pretty happy with a mut_ prefix for mutable versions. newthing = oldthing.push(foo) anything.mut_push(foo) x = bagofun.sort() bagosadness.mut_sort() etc. Advantages: - consistent meaning with the 'mut' keyword. - works with pretty much any name. - makes mutable versions just a bit uglier than immutable code. Disadvantages: - If an API is inherently mutable, it gets pretty noisy. - A bit ugly, probably turns off newcomers. - If the compiler warns on ignoring return values, and mutating methods return (), then a convention might be unnecessary. --Jeff What about establishing a convention that mutable methods start with an uppercase letter while non-mutating methods start with a lowercase letter? It would be lightweight in terms of character count/looks and at the same time give mutable methods a slight visual emphasis, which makes sense I think. I know this convention is already used by Traits, but when I looked through some code with the above proposal in mind it would be easy to distinguish between these two uses of the same convention due to the differing contexts they're used in. -Eric ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] mutable vs. functional APIs
The new Path API still has a return a new path set of APIs. They're just named differently. For example, the old path.push(foo) is now path.join(foo). And all the path.set_*() mutating methods have variants path.with_*() that return a new path. -Kevin On Oct 18, 2013, at 9:09 AM, Jack Moffitt j...@metajack.im wrote: In the latest Rust upgrade for Servo, I noticed that the path API is now mutate-in-place instead of return a new path. It used to be that path.push(foo) gave you a new path with an extra component, but now path.push(foo) returns () and mutates the path in place. I liked the old API better. I realize this is probably more consistent with std::vec, and consistency is good. I thought I'd bring this up to see what other people thought as a lot of the APIs are getting rewritten lately and I haven't seen any concrete guidelines on what they should be. 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. jack. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev smime.p7s Description: S/MIME cryptographic signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] mutable vs. functional APIs
On Fri, Oct 18, 2013 at 12:09 PM, Jack Moffitt j...@metajack.im wrote: In the latest Rust upgrade for Servo, I noticed that the path API is now mutate-in-place instead of return a new path. It used to be that path.push(foo) gave you a new path with an extra component, but now path.push(foo) returns () and mutates the path in place. I liked the old API better. I realize this is probably more consistent with std::vec, and consistency is good. I thought I'd bring this up to see what other people thought as a lot of the APIs are getting rewritten lately and I haven't seen any concrete guidelines on what they should be. 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. jack. I think an immutable version should only be exposed if it's as efficient as operating in-place. For containers, I really don't want to have APIs that exist only to make allocating a whole new container and copying over more convenient. It's harder to write efficient code when inefficient code looks more idiomatic. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] mutable vs. functional APIs
On Oct 18, 2013, at 11:14 AM, Daniel Micay danielmi...@gmail.com wrote: On Fri, Oct 18, 2013 at 12:09 PM, Jack Moffitt j...@metajack.im wrote: In the latest Rust upgrade for Servo, I noticed that the path API is now mutate-in-place instead of return a new path. It used to be that path.push(foo) gave you a new path with an extra component, but now path.push(foo) returns () and mutates the path in place. I liked the old API better. I realize this is probably more consistent with std::vec, and consistency is good. I thought I'd bring this up to see what other people thought as a lot of the APIs are getting rewritten lately and I haven't seen any concrete guidelines on what they should be. 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. jack. I think an immutable version should only be exposed if it's as efficient as operating in-place. For containers, I really don't want to have APIs that exist only to make allocating a whole new container and copying over more convenient. It's harder to write efficient code when inefficient code looks more idiomatic. I agree wholeheartedly with this. Path needed to preserve the convenient “functional update” methods, because creating new paths based on existing immutable ones is rather common. But the APIs are definitely not more convenient than the mutation ones, just as convenient. Regarding Jeff’s suggestion for a mut_ prefix, that really only makes sense to me when returning some form of mut value. Although that would typically be .as_mut_foo() rather than .mut_foo(). In general, I think method names should make it reasonably obvious whether it’s mutating or not without having to use “mut_”. In the case of Path, the name .push() has a long history of mutating the receiver, so Path.push() mutates. -Kevin___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] mutable vs. functional APIs
On 10/18/2013 11:14 AM, Daniel Micay wrote: On Fri, Oct 18, 2013 at 12:09 PM, Jack Moffitt j...@metajack.im mailto:j...@metajack.im wrote: In the latest Rust upgrade for Servo, I noticed that the path API is now mutate-in-place instead of return a new path. It used to be that path.push(foo) gave you a new path with an extra component, but now path.push(foo) returns () and mutates the path in place. I liked the old API better. I realize this is probably more consistent with std::vec, and consistency is good. I thought I'd bring this up to see what other people thought as a lot of the APIs are getting rewritten lately and I haven't seen any concrete guidelines on what they should be. 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. jack. I think an immutable version should only be exposed if it's as efficient as operating in-place. For containers, I really don't want to have APIs that exist only to make allocating a whole new container and copying over more convenient. It's harder to write efficient code when inefficient code looks more idiomatic. Efficiency isn't the only design consideration and not always the most important, even in Rust. I imagine that path manipulation is not a performance bottleneck for most applications. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] mutable vs. functional APIs
On Fri, Oct 18, 2013 at 6:09 PM, Jack Moffitt j...@metajack.im 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] mutable vs. functional APIs
To be clear, '!' doesn't mean mutation, it means 'dangerous.' For example, some methods return nil on error, and the bang version throws an excption on error. Sometimes, mutation is dangerous, though... ;) ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev