Fast, dangerous, mutable vs safe, slow, immutable. #19
Replies: 4 comments 4 replies
-
Just to add, you might also want to consider what's the return type of the mutable variants Namely |
Beta Was this translation helpful? Give feedback.
-
Another alternative that I just remember is to have something like (don't take this example too literally): trait Vector2D {
def getX(): Double
def getY(): Double
}
trait MutVector2D extends Vector2D {
def setX(x: Double): this.type
def setY(y: Double): this.type
} This is closer to what Rust does with def foo(vec: Vector2D): Double
def bar(vec: MutVector2D): Double You know that |
Beta Was this translation helpful? Give feedback.
-
My own vote, is to either
e.g. i.e. When we discussed this in the past however,
It remains my preference .... with @JD557 suggestion of the Would be the equivelant I guess. From what I can see, and designed against exactly this sort of use case... I'd support leaning into re-using that work in stdlib. My only real preference is that it's very clear at the callsite, which flavour of |
Beta Was this translation helpful? Give feedback.
-
Come to think of it, this looks really weird: v0 + v1 += v2 I'll change return types to Unit on mutating operations and experiment with splitting the mutable and immutable extension methods into separate packages. |
Beta Was this translation helpful? Give feedback.
-
The S.L.A.S.H. (Scala Linear Algebra & Statistics Hacks) library supports two broad kinds of operations:
To improve accessibility, we want to find a way to clearly delineate the category of each and every operation.
For vector operations like
+=
and+
Scala provides natural ways to cue users about whether the operation mutates or copies data. However, ambiguity pools up around named operations likedef clamp(min:Double, MAX:Double)
,def min(minimum:Double)
, ordef MAX(maximum:Double)
.Should we follow the example of Scala collections? That splits the Vector and Matrix types into two packages: mutable and immutable. This approach drastically increases complexity; even under a condition of total separation of these two packages, it doubles the number of types to document, test, and maintain. However, without total separation, complexity increases exponentially wherever these two families of types interact. For example, consider addition between a mutable vector and an immutable vector:
What type should the result have:
mutable.Vec[3]
orimmutable.Vec[3]
? What's more, now the+
operation has to somehow support 4 cases:mv + mv
,iv + iv
,mv + iv
,iv + mv
, where it used to only support 1:v + v
.Rather, what if we take the hint from
ArrayOps
?ArrayOps.sort
mutates data in place andArrayOps.sorted
yields a sorted copy. Can we rely on the "ed" suffix to designate all of the immutable operations?It seems reasonable to provide mutable
clamp
alongside immutableclamped
, but that implies mutablemin
&MAX
beside immutablemined
&MAXed
. In particular, the word "mined" can mean a lot of things, but none of them imply: "Returns a copy of this vector with all elements greater than or equal to a specified minimum."Instead of an English language suffix, the documentation in the Julia programming language proposes a convention of marking unsafe methods that may mutate their parameters with an exclamation point:
"Functions can modify (mutate) the contents of the objects their arguments refer to. (The names of functions which do this are conventionally suffixed with '!'.)"
https://docs.julialang.org/en/v1/base/base/#Keywords
The exclamation point might not suit S.L.A.S.H. which already defines
!
as a call to the factorial method, but maybe some other symbol?More generally, if we decide to label operations as unsafe/mutable vs. safe/immutable, should we consider one as default and mark the other? As an analogy, should we force users who prioritize performance to read warning labels every time they move or should we require the people who put safety first to put soft padding on everything?
Alternatively, since all of these operations take the form of extension methods, maybe we should just separate the mutable extensions from the immutable ones? One anticipates all sorts of naming collisions in scopes that may need to use both.
Suppose, instead, we create S.L.A.S.H as a mutable only library, then create another one that wraps it with immutable/safe abstractions?
Should we better just not worry about any of this?
How would you address this accessibility concern?
Beta Was this translation helpful? Give feedback.
All reactions