Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
sarehu
Apr 20, 2007

(call/cc call/cc)

Jabor posted:

I'm not sure why you'd claim C++ somehow has better immutability

Where did I claim that?

What's different about C++'s const keyword is that it actually means something, because in C++, you have real mutability built right into the language. Rust doesn't have that. Rust's mutable borrows are essentially syntactic sugar, they give you no new capabilities that you'd have lacked without them. In particular, well, first, let's suppose Rust had "out" parameters (like C# has) so that you can conveniently return extra values without having to add new fields to the return type. (They have to be returned into variables that have been declared but haven't been given a value yet.)

Then, instead of writing

code:
fn foo(x : &mut Bar) { ...
you could write

code:
fn foo(x : Bar, x_out : out Bar) { ...
And then in usage, you write

code:
  let x : Bar = ...;
  let y : Bar;
  foo(x, out y);
I should point out that at this point x doesn't have a value, it's in some de-initialized state, since it got moved into the parameter list of foo, and y does have a value. So in any sane language (maybe with "let mut x") you could then write

code:
  x = y;
which moves y into x, putting x back into an initialized state and y into a de-initialized state. Whether you can actually do that or not is moot -- you can just use the variable y afterward, instead, unless x is outside a loop, but at any rate mutable local variables and loops are just a concise way to write explicitly tail-recursive functions.

In the function call, x got moved into the parameter list, and became de-initialized. So you could have x also be used as the output parameter

code:
foo(x, out x);
This is a simple example of how a mutable borrow can get turned into in in/out parameter pair. You can also turn
code:
foo(&mut z.some_field)
into code like this:

code:
let {a, some_field, b} = z;
let blah : Bar;
foo(some_field, out blah);
z = {a, blah, b};
You can do the same with Vec<T>'s. Instead of having v.push(x) you have push(v, x, out v). Instead of foo(v.get_mut(j));, the equivalent is

code:
let {value : T, v_rest : RestOfVec<T>} = get_mut(&v, j);
let tmp : T;
foo(value, out tmp);
v = recombine(tmp, v_rest);
So you still get an O(1) API there too (you don't have to split the vec in O(n) and reconcatenate the whole thing).

So, in this language without mut (which happens to be just as as powerful as one with mut), what do you do when you really really do want to mutate something? You make a type like this:

code:
enum RefCell<T> {
  ExclusivelyBorrowed,
  Shared(refcount : usize, value : T),
}
Then (using unsafe code) you make functions fn borrow(x : &'a RefCell<T>) -> Ref<'a, T> and fn borrow_excl(x : &RefCell<T>) -> T and fn unborrow_excl(x : &RefCell<T>, value : T). You have runtime checks to make sure there's no pointers into T, or pointers into some substructure of T, when you want to mutate it, so it's memory safe.

So that's the real mutability situation. Mutable borrows are an illusion, real mutability comes from RefCell and anything can be mutable if it wants to be.

P.S. The Rust docs are poo poo.

P.P.S. Oh and now I've discovered that Cell<T> exists too, for primitive types that don't have any sub-structure, which means you can directly overwrite them without checking for (or having) exclusive ownership.

Edit: Things get a bit sketchy, however, when you want to use unsized types. It seems like you'd have to pay something, such as a runtime assertion or needless heap allocation -- something that's different than just memcopying a bunch of extra times -- or you'll lose certain kinds of genericity.

sarehu fucked around with this message at 12:44 on Oct 11, 2015

Adbot
ADBOT LOVES YOU

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder
:eyepop:


yo i've never read an actual semi-academic book about programming. i'm gonna try to do one hour a day on something somewhat difficult. I'd be interested in either algorithms, type theory stuff, maybe some math stuff, idk. or maybe something about how computer programs actually work because i vaguely know what the heap and the stack are but after that i'm pretty loving confused.

any terrible recommendations?

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder
also are there any GOOD programming forums that aren't this and cavern of cobol? that are maybe a little more active?

gonadic io
Feb 16, 2011

>>=

MALE SHOEGAZE posted:

also are there any GOOD programming forums that aren't this and cavern of cobol? that are maybe a little more active?

I've heard that gamesutra is pretty good for that niche, dunno about general programming though

cinci zoo sniper
Mar 15, 2013




gonadic io posted:

I've heard that gamesutra is pretty good for that niche, dunno about general programming though
gamasutra, while being legit good, has no forums

i think whole 'coding forums' thing now mainly sits on reddit, outside of regional communities

zokie
Feb 13, 2006

Out of many, Sweden

gonadic io posted:

ya, no wannabe functional langs like scala, f#, etc stop you from mutating objects inside supposedly pure data structures.

haskell does, and i want to say that erlang might? even ocaml doesn't.

When I see people do stuff like this, I start hating ReSharper a little bit more
code:

readonly List<Foo> foos = new List<Foo>();

Same with wanting to sprikle static everywhere!

distortion park
Apr 25, 2011


zokie posted:

When I see people do stuff like this, I start hating ReSharper a little bit more
code:

readonly List<Foo> foos = new List<Foo>();

Same with wanting to sprikle static everywhere!

Idgi, what's wrong with that?

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

pointsofdata posted:

Idgi, what's wrong with that?

It's pretty misleading because while you cannot outright reassign 'foos' to a different list, you can still just .Clear() it and fill it with different elements, which 99% of the time is the same thing.

What you almost always want to do is:

code:
IReadOnlyList<Foo> foos = new List<Foo>();

VikingofRock
Aug 24, 2008




sarehu posted:

That's because x is an integral type. But with your own type Foo, you can write

code:
fn main() {
    let x = Foo::new();
    display(&x);
    do_a_modify(&x);
    display(&x);
}
such that the first call to display will print 1, and the second will print 2.

The use of let also acts like the way "final" does on local variable declarations in Java, so you can't just brazenly assign to it, but the mutability of the object itself is a question of that type's API decision.

Maybe it's because I am super jet lagged, but I still don't understand what you are saying here.

Rust code:
#[derive(Debug)]
struct Foo {
    value: u8
}

impl Foo {
    fn new() -> Foo {
        Foo { value: 1 }
    }
}

fn display(foo: &Foo){
    println!("{:?}", foo);
}

fn do_a_modify(foo: &Foo){
    foo.value += 1;
}

fn main() {
    let x = Foo::new();
    display(&x);
    do_a_modify(&x);
    display(&x);
}
(playground link)

This doesn't compile, because of the modification of foo.value on line 17. But I'm assuming this isn't exactly what you are talking about--could you fix up this example so that it demonstrates what you are talking about?

pepito sanchez
Apr 3, 2004
I'm not mexican

quote:

What you almost always want to do is

what about unmodifiable lists in that case?

doesn't it just depend on what you want to do?

Brain Candy
May 18, 2006

MALE SHOEGAZE posted:

i'm gonna try to do one hour a day on something somewhat difficult.

project Euler in a lang you don't know

MALE SHOEGAZE posted:

or maybe something about how computer programs actually work because i vaguely know what the heap and the stack are but after that i'm pretty loving confused.

try Eric lipperts blog

~Coxy
Dec 9, 2003

R.I.P. Inter-OS Sass - b.2000AD d.2003AD
I think the original complaint is that R# suggests const because there are no more assignments, so the programmer dutifully adds it to avoid being nagged by the squiggly line without thinking *why*

Bloody
Mar 3, 2013

zokie posted:

When I see people do stuff like this, I start hating ReSharper a little bit more
code:

readonly List<Foo> foos = new List<Foo>();

Same with wanting to sprikle static everywhere!

same but the lack of var

distortion park
Apr 25, 2011


NihilCredo posted:

It's pretty misleading because while you cannot outright reassign 'foos' to a different list, you can still just .Clear() it and fill it with different elements, which 99% of the time is the same thing.

What you almost always want to do is:

code:
IReadOnlyList<Foo> foos = new List<Foo>();

Now someone can just replace the list instance though and your referenced might be inconsistent. Presumably in the first example you used List instead of a Read-only list for a reason - there's nothing wrong with making a reference to that list read-only.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:


im probably missing something here, but how have you shown that &mut doesn't actually mean anything? youve just shown how a hypothetical language that isn't rust could do the same thing with different syntax?

sarehu
Apr 20, 2007

(call/cc call/cc)

VikingofRock posted:

This doesn't compile, because of the modification of foo.value on line 17. But I'm assuming this isn't exactly what you are talking about--could you fix up this example so that it demonstrates what you are talking about?

code:
use std::cell::Cell;

#[derive(Debug)]
struct Foo {
    value: Cell<u8>
}

impl Foo {
    fn new() -> Foo {
        Foo { value: Cell::new(1) }
    }
}

fn display(foo: &Foo){
    println!("{:?}", foo);
}

fn do_a_modify(foo: &Foo){
    foo.value.set(foo.value.get() + 1);
}

fn main() {
    let x = Foo::new();
    display(&x);
    do_a_modify(&x);
    display(&x);
}

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

pepito sanchez posted:

what about unmodifiable lists in that case?

doesn't it just depend on what you want to do?

yes, access control =/= immutability


access control = have a private List and a public IReadOnlyList reference to that list

immutability = have a private/public/whatever IReadOnlyList that gets populated during object construction and then cannot be messed with again, whether by internal or external methods

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

sarehu posted:

code:
use std::cell::Cell;

#[derive(Debug)]
struct Foo {
    value: Cell<u8>
}

impl Foo {
    fn new() -> Foo {
        Foo { value: Cell::new(1) }
    }
}

fn display(foo: &Foo){
    println!("{:?}", foo);
}

fn do_a_modify(foo: &Foo){
    foo.value.set(foo.value.get() + 1);
}

fn main() {
    let x = Foo::new();
    display(&x);
    do_a_modify(&x);
    display(&x);
}

but this is what i was originally trying to say. if you want mutability then you need to be explicit about it. if you're wrapping values in a Cell then you're being explicit about it and you can't accidentally mix that with normal values:
code:
let y = x.value * 2;
code:
error: binary operation `*` cannot be applied to type `core::cell::Cell<u8>` [E0369]
<anon>:27     let y = x.value * 2;
                      ^~~~~~~
error: aborting due to previous error

VikingofRock
Aug 24, 2008




sarehu posted:

code:
use std::cell::Cell;

#[derive(Debug)]
struct Foo {
    value: Cell<u8>
}

impl Foo {
    fn new() -> Foo {
        Foo { value: Cell::new(1) }
    }
}

fn display(foo: &Foo){
    println!("{:?}", foo);
}

fn do_a_modify(foo: &Foo){
    foo.value.set(foo.value.get() + 1);
}

fn main() {
    let x = Foo::new();
    display(&x);
    do_a_modify(&x);
    display(&x);
}

How is that different from the following C++?

C++ code:
#include <iostream>

class Foo {
	public:
		Foo() : value_(1) {}

		void increment() const {
			value_ += 1;
		}

		int value() const {
			return value_;
		}

	private:
		mutable int value_;
};

void display(const Foo& foo) {
	std::cout << "Foo value: " << foo.value() << std::endl;
}

void do_a_modify(const Foo& foo) {
	foo.increment();
}

int main() {
	Foo foo;
	display(foo);
	do_a_modify(foo);
	display(foo);
}
(ideone link)

sarehu
Apr 20, 2007

(call/cc call/cc)

fart simpson posted:

im probably missing something here, but how have you shown that &mut doesn't actually mean anything? youve just shown how a hypothetical language that isn't rust could do the same thing with different syntax?

Specifically, that hypothetical language is Rust minus mutable borrows. Or, more strongly, Rust without mutable borrows, without mutable locals, but with Clojure-style recur syntax. And RefCell and Cell end up being the same thing with the same purposes -- "true" mutability instead of just some Haskell programmer's dream of how systems programming should be with linear types and implicit runST everywhere.

sarehu
Apr 20, 2007

(call/cc call/cc)

VikingofRock posted:

How is that different from the following C++?

That's bad C++ and it breaks the rules of const. For example the C++11 standard library can make certain assumptions about const methods, see 17.6.5.9/3. On the other hand, the Rust code is perfectly fine because there's no expectation that having a &Foo means you can't mutate the Foo.

VikingofRock
Aug 24, 2008




sarehu posted:

That's bad C++ and it breaks the rules of const. For example the C++11 standard library can make certain assumptions about const methods, see 17.6.5.9/3. On the other hand, the Rust code is perfectly fine because there's no expectation that having a &Foo means you can't mutate the Foo.

Personally I would claim that that is bad Rust as well, and I would expect that in Rust &Foo would be semantically immutable unless being semantically mutable in immutable contexts was the whole point of Foo. Maybe this is a poor assumption on my part, though. Are there any examples in the Rust standard library (besides Cell, RefCell, and cousins) where &Foo is semantically mutable?

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

sarehu posted:

That's bad C++ and it breaks the rules of const. For example the C++11 standard library can make certain assumptions about const methods, see 17.6.5.9/3. On the other hand, the Rust code is perfectly fine because there's no expectation that having a &Foo means you can't mutate the Foo.

are you sure about this? the only examples you've given of a mutable &Foo have been in your hypothetical version of rust with a different syntax for mutable borrows and when stuff is wrapped in a Cell or RefCell, which are explicitly "Shared mutable containers"

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

quote:

The more common inherited mutability, where one must have unique access to mutate a value, is one of the key language elements that enables Rust to reason strongly about pointer aliasing, statically preventing crash bugs. Because of that, inherited mutability is preferred, and interior mutability is something of a last resort.

"inherited mutability" is referring to mutable borrows, and interior mutability is using Cell or RefCell. so like, you can wrap all your rust code in RefCells and Rc and get mutability everywhere and defer errors to runtime, but why would that be considered "perfectly fine"? you'd be giving up a big portion of the compelling part of rust, and it wouldnt even be easier to code in that style

Vanadium
Jan 8, 2005

a cool thing about mutable borrows is that you can statically only have one active at a time of a thing

also i think rust's immutability such asit is is mostly about ensuring memory safety, not some higher-level idea of "this semantically shouldn't change"

sarehu
Apr 20, 2007

(call/cc call/cc)

fart simpson posted:

are you sure about this? the only examples you've given of a mutable &Foo have been in your hypothetical version of rust with a different syntax for mutable borrows and when stuff is wrapped in a Cell or RefCell, which are explicitly "Shared mutable containers"

The example I gave was in the real version of Rust...

If you want something akin to C++11's const, which is immutable or at least acts immutable, Rust has the Sync trait for that (I think). And, as with C++, you have to declare it unsafely for your bespoke types. Read the link, it lays down expectations about the mutability of arbitrary non-Sync &Foo pretty clearly.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

yes, but it was wrapped in a Cell, which is explicitly a mutable container?

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

sarehu posted:

The example I gave was in the real version of Rust...

If you want something akin to C++11's const, which is immutable or at least acts immutable, Rust has the Sync trait for that (I think). And, as with C++, you have to declare it unsafely for your bespoke types. Read the link, it lays down expectations about the mutability of arbitrary non-Sync &Foo pretty clearly.

i think you have this backwards. pretty much everything is Sync by default:

quote:

Types that are not Sync are those that have "interior mutability" in a non-thread-safe way, such as Cell and RefCell in std::cell. These types allow for mutation of their contents even when in an immutable, aliasable slot, e.g. the contents of &Cell<T> can be .set, and do not ensure data races are impossible, hence they cannot be Sync.
...
Any types with interior mutability must also use the std::cell::UnsafeCell wrapper around the value(s) which can be mutated when behind a & reference; not doing this is undefined behaviour (for example, transmute-ing from &T to &mut T is invalid).

sarehu
Apr 20, 2007

(call/cc call/cc)

fart simpson posted:

yes, but it was wrapped in a Cell, which is explicitly a mutable container?

Yeah, and Foo was thus mutable. Foo isn't "explicitly" mutable unless you look at its source code. You have to have personal information about the type to know that it's immutable, just like how in C# or Java you know that String is immutable.

Luigi Thirty
Apr 30, 2006

Emergency confection port.

Sagacity posted:

It's an interesting read! It looks more and more like Ken Silverman lucked out by creating the Build engine just at the right time, but not because of any discernible coding skills outside the realm of 2.5d rendering algorithms. I guess that's why John Carmack and Tim Sweeney had a bit more staying power.

there's pretty clearly a spectrum (hurr hurr) here

john carmack - :spergin: but in the way that means he worked 24 hours a day and vacuumed up 600-page books and siggraph lectures
ken silverman - :spergin: but in the way that everything he does is idiosyncratic and he refuses outside assistance
tim sweeney - he made 2d dos games in his basement and made enough money doing it that he could publish other people's 2d dos games

sarehu
Apr 20, 2007

(call/cc call/cc)
Oh man. I always wondered what the deal was with Silverman prototyping stuff in BASIC.

Valeyard
Mar 30, 2012


Grimey Drawer
I miss the pos

VikingofRock
Aug 24, 2008




sarehu posted:

Yeah, and Foo was thus mutable. Foo isn't "explicitly" mutable unless you look at its source code. You have to have personal information about the type to know that it's immutable, just like how in C# or Java you know that String is immutable.

I guess I don't really see the difference between having interior mutability via Cell in Rust, and having interior mutability via mutable in C++ or even via unsafePerformIO in Haskell. Exposing exterior mutability via any of those is pretty unidiomatic as far as I can tell (though we can debate about the degree of unidiomaticness), but they are all parts of their respective languages. It seems pretty arbitrary to draw a line and say that Rust does not have immutability because of Cell, but that C++ and Haskell do.

But at this point I think we are just debating the definition of immutability, which to me is sort of a boring debate, so I'll drop it.

vodkat
Jun 30, 2012



cannot legally be sold as vodka
can someone help me with my terrible programming?

I keep getting this error on my lovely little scraper and I have no I have no idea what it means or what the problem is. It runs fine in ipython but when I run it in the terminal I get this error:

code:
Traceback (most recent call last):
  File "scraper.py", line 249, in <module>
    pageid = frankid(x)
  File "scraper.py", line 233, in frankid
    x = re.sub("http://search.knightfrank.co.uk/", "", url)
  File "/Users/admin/anaconda/lib/python2.7/re.py", line 155, in sub
    return _compile(pattern, flags).sub(repl, string, count)
TypeError: buffer size mismatch
I think the relevant bits of code are:

code:

def frankid(url):
    x = re.sub("http://search.knightfrank.co.uk/", "", url)
    return x

for x in houseurls:
    pageid = frankid(x)
It should just be a quick bit of cleaning on a url, I don't get why its going wrong?

gonadic io
Feb 16, 2011

>>=
well no language at all has absolute immutability, as you could always hexedit memory or whatever. it's like how java's "finally" isn't absolutely guaranteed to happen, as there's lots of things that is outside of the computer's control - what about it losing power for example?

haskell calls itself immutable and referentially transparent, but that doesn't take into account out-of-memory errors, etc ,etc. At the end of the day, these guarantees are ALWAYS made with caveats - in haskell you can assume that there generally isn't usage of the unsafe functions that's actually unsafe, or using the ffi, or anything.

having a method that can mutate a supposedly immutable object doesn't seem to be something that's forbidden in c++ or rust, whereas it it's "banned" (but you technically can do it) in haskell. that's the difference as i see it. are there any languages that do their best to not have any escape hatches to the type system/semantics? as I said you can always edit memory so no lang can guarantee it 100%.

gonadic io
Feb 16, 2011

>>=
would it be desirable, as thanks to the incompleteness things you could always have a situation in which the compiler can't prove something but you're sure that it's true. agda, coq, idris, all the proof language have "i'm overruling you compiler" functions or flags or what have you i think.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

gonadic io posted:

would it be desirable, as thanks to the incompleteness things you could always have a situation in which the compiler can't prove something but you're sure that it's true. agda, coq, idris, all the proof language have "i'm overruling you compiler" functions or flags or what have you i think.

this is what unsafe blocks in rust are for, when you need to do something you know is memory safe but beyond provability to the compiler

kitten emergency
Jan 13, 2008

get meow this wack-ass crystal prison

vodkat posted:

can someone help me with my terrible programming?

I keep getting this error on my lovely little scraper and I have no I have no idea what it means or what the problem is. It runs fine in ipython but when I run it in the terminal I get this error:

code:

Traceback (most recent call last):
  File "scraper.py", line 249, in <module>
    pageid = frankid(x)
  File "scraper.py", line 233, in frankid
    x = re.sub("http://search.knightfrank.co.uk/", "", url)
  File "/Users/admin/anaconda/lib/python2.7/re.py", line 155, in sub
    return _compile(pattern, flags).sub(repl, string, count)
TypeError: buffer size mismatch

I think the relevant bits of code are:

code:


def frankid(url):
    x = re.sub("http://search.knightfrank.co.uk/", "", url)
    return x

for x in houseurls:
    pageid = frankid(x)

It should just be a quick bit of cleaning on a url, I don't get why its going wrong?

make sure that url parameter is actually a string

vodkat
Jun 30, 2012



cannot legally be sold as vodka

uncurable mlady posted:

make sure that url parameter is actually a string

Thanks! My problem really was that stupid/simple.

Adbot
ADBOT LOVES YOU

DONT THREAD ON ME
Oct 1, 2002

by Nyc_Tattoo
Floss Finder
i really hate python backtraces but i think that's because my primary exposure to them is currently as a response field in a bad api

  • Locked thread