logoalt Hacker News

Waterluviantoday at 4:32 PM8 repliesview on HN

The worst are methods that both mutate and return values.

I know this gets into a complex land of computer science that I don’t understand well, but I wish I could define in TypeScript “any object passed into this function is now typed _never_. You’ve destroyed it and can’t use it after this.” Because I sometimes want to mutate something in a function and return it for convenience and performance reasons, but I want you to have to reason about the returned type and never again touch the original type, even if they are the same object.


Replies

thaynetoday at 6:14 PM

> any object passed into this function is now typed _never_. You’ve destroyed it and can’t use it after this.

That is basically what affine types are. Once the value is "consumed" it can't be used again.

In rust, this is expressed as passing an "owned" value to a function. Once you pass ownership, you can't use that value anymore.

And having used it in rust, I wish more languages had something like that.

show 1 reply
Aurornistoday at 5:19 PM

> but I wish I could define in TypeScript “any object passed into this function is now typed _never_.

Having explicit language to differentiate between pass by reference and pass by value avoids this confusion. It requires a little more thought from the programmer but it’s really minimal once you internalize it.

Rust takes this a step further with an explicit ownership and borrowing model. The compiler will refuse your code if you try to write something that that violates the borrow checker. This is endlessly frustrating to beginners but after adapting your mind to ownership safety you find yourself thinking in the same way in other languages.

I always found real-world JavaScript codebases frustrating because there was so much sharing that wasn’t entirely intentionally. It only got fixed when someone recognized a bug as a result.

show 1 reply
vimwizardtoday at 4:48 PM

Rust ownership model ("stacked borrows" I believe it's called) is basically this

show 3 replies
magniotoday at 4:46 PM

What you are describing is linear (or affine) types in academic parlance, where a value must be used exactly (or at most) once, e.g., being passed to a function or having a method invoked, after which the old value is destroyed and not accessible. Most common examples are prolly move semantics in C++ and Rust.

ecshafertoday at 4:40 PM

ruby has the convention of ! for dangerous destructive or mutating methods. This is something that I wish would spread around a bit.

For example:

# Original array

array = [1, 2, 3]

# Using map (non-destructive)

new_array = array.map { |x| x * 2 }

# new_array is [2, 4, 6]

# array is still [1, 2, 3] (unchanged)

# Using map! (destructive)

array.map! { |x| x * 2 }

# array is now [2, 4, 6] (modified in-place)

show 1 reply
Xenoamorphoustoday at 6:04 PM

> The worst are methods that both mutate and return values

Been bitten a few times by Array.sort().

Luckily there’s Array.toSorted() now.

show 1 reply
z3t4today at 7:44 PM

If you want to upset people on the internet tell them that JavaScript is strongly typed, immutable, and everything is passed by value. Which is true. You can change member values though, which is the footgun.

daricktoday at 5:09 PM

This is possible with the asserts x is y pattern no?

https://www.typescriptlang.org/play/?#code/C4TwDgpgBAYg9nKBe...

show 1 reply