Top
Best
New

Posted by ibobev 1 day ago

Why isn't mutable a subtype of immutable, or vice versa?(crumbles.blog)
22 points | 33 comments
js8 1 hour ago|
I feel like the explanation is overcomplicated. Types are properties of values, not variables. Mutability is a property of variable, not of a value.

So it's kind of a categorical error. (I want to joke here that all categorical errors are just type errors in category theory.) When we speak of "type of a variable", we mean this variable can only be assigned (bound to) values of certain type. This has nothing to do with whether it can be reassigned (i.e. mutability).

So you don't even need the notion of subtyping to explain this.

Also, one could probably define variable as a monad over its type.

pxeger1 1 hour ago||
This post is about data structures, which are values, but like variables, they also contain values. Therefore they can be mutable. You can argue that a mutable data structure is an object not a value, I suppose. But it can go in the same place as a value, so it makes sense to talk about subtyping.
rtpg 1 hour ago|||
Experimentally, at least one major programming language (Rust) places mutability into the type system.

Whether or not something belongs into a type system is ultimately determined by the type system. We can choose whether or not mutability is considered a part of a type.

> When we speak of "type of a variable", we mean this variable can only be assigned (bound to) values of certain type. This has nothing to do with whether it can be reassigned (i.e. mutability).

This is a bit too simplistic IMO. You're talking about name bindings, the article is talking more about things like interior mutability.

Rebinding a name is ... generally not a type system concern by my understanding.

js8 57 minutes ago||
You have a point; I am looking at it from quite functional programming perspective, because that's how type systems are typically understood. So from that perspective, interior mutability is a form of rebinding.

When you say "we can choose mutability as a part of a type", the question is, what kind of errors are we trying to prevent? What is the semantics we want to give? From that it should be obvious whether it can be subtype or not.

ffsm8 23 minutes ago||
> So from that perspective, interior mutability is a form of rebinding

really? i honestly dont have _too_ much experience with functional languages, basically only elixir and consequently some amount of erlang in that vain... but i'd feel like thats not the same? but it may be that my point of view is too narrow.

from my experience with that functional language, the equivalent to this scenario would be a struct - and wherever i can mutate properties within it -- or need to reconstruct the struct from scratch.

both have technical consequences, eg if i passed the struct into a consumer somewhere which keeps it, it would get the "modified" version automatically when the property was changed

but on reconstruction, it'd have to introduce some kind of event listener to handle the reconstruction.

simple example for such a scenario would be eg a session within a SSE api. the mutated struct would trivially allow for an uninterrupted stream no matter how long the session is extended, the latter needs to pay attention so its not opening a memory leak to support that feature.

noelwelsh 1 hour ago||
A nitpick: Types are properties of expressions, not values. Type errors happen at compile time, before code runs. Values only exist at run time.
js8 1 hour ago|||
Fair enough. Although I don't fully subscribe to the dichotomy of compile vs run time, we can say that.
pxeger1 1 hour ago||||
In semantics, types are properties of values and expressions. Type safety is about whether the type of an expression always matches the type of the value it evaluates to.
jounker 37 minutes ago|||
Doesn’t this break down with dependent type systems?
bruce343434 47 minutes ago||
So really there are 2 orthogonal axes:

- A: can I change the value

- B: can something else change the value (can I depend on a predictable stable value)

Because the axes are orthogonal, hierarchical based subtyping (inheritance) breaks, but type classes (interfaces), ad hoc polymorphism, would work.

In C, const answers A

In rust, due to pointer aliasing restrictions (either one mut pointer xor any amount of read only pointers), (lack of) mut answers both A and B

raffael_de 3 minutes ago||
as far as my set theoretical intuition goes, immutable has to be a subtype of mutable, if anything.
Sharlin 38 minutes ago||
As a total aside, the names `car` and `cdr` for "head" and "tail" are honestly some of the most baffling historical relics in all of computing.
MiroslavPokorny 31 minutes ago|
What i dont understand is why does everything have to be so brief. Take car and cdr there is only one character difference, more character differences is a good thing not bad.
gumby 14 minutes ago|||
More short names fit in your fovea than long names so for the most common operations they are the easiest to read. It’s the same reason the most common words in human languages are the shortest (and programming languages are for humans to use so subject to the same pressure.

Also, Back In The Old Days memory was very expensive and precious so short identifiers were important. Often labels were tightly constrained, for example being limited to six upper-case characters so they would fit in a single word.

Sharlin 12 minutes ago||||
The usual convention (which also occurs in natural languages) is that often-used words should be short, rarely used should (and can afford to be) more descriptive.
thaumasiotes 19 minutes ago|||
> What i dont understand is why does everything have to be so brief.

The concept is to make things easier for people who are familiar with the language. Making things harder for that group is always counterproductive, because they are the only people who can do any useful work.

BlackFly 2 hours ago||
If you pair mutability with exclusive access then you have handled the objection raised by the (some may say overly) strict definition of subtyping here and also the attempt to argue over the objection. No code which asks for an immutable instance will ever observe the mutability because the ask for an immutable reference is exclusive. Therefore, you could pass the mutable reference but so long as something holds onto that reference the mutability is no longer available to other code.

So mutability xor aliasing provides this strict subtyping relation. Of course, you also then need ways of loosening this by providing objects without such a contract and you enter the land of interior mutability, where again the mutable methods can be understood as a part of a subtype because a holder of the reference without mutable methods was explicitly told that there was no the guarantee that the object wouldn't change.

anankaie 59 minutes ago||
The cons example is conflating implementation details (return the value passed in to cons) with the semantics (return the left-side of this pair that was instantiated by cons). In the first case it is a category error to think about mutability. In the second mutability makes perfect sense.

Moreover, I suspect it is possible to construct an interface such that to prove statically that you can Liskov Substitute a type into it would be equivalent to deciding Halt: All you need are extensional semantics in your type system.

Mikhail_Edoshin 1 hour ago||
So the problem is that the semantic of methods (car, cdr) is not well defined: it is either "give me that member" or "give me that member and guarantee next time the result will be same". And the solution is to clearly separate those by adding more method names.
raincole 2 hours ago||
In other words, immutable != read only.

In C# ReadOnlyCollection<T> and ImmutableArray<T> are two completely different things for this exact reason.

gus_massa 14 hours ago||
Yep, I wondered that for a few optimizations in Racket. It's harder than it looks, probably something about covariant or contravariant types, I gave up.
MiroslavPokorny 29 minutes ago|
Because if mutable is a sub type of immutable, it wouldnt be right to give a mutable value when the receiver expected immutable values. The same is also true if one reverses the relationship, the mutable operations should not be defined on the immutable, and if they were that would be dumb because the immutable implementations should fail, which is silly.
More comments...