Hacker News new | ask | show | jobs
by nmadden 689 days ago
I don’t know how Swift and Koka handle things, but I’ve written a lot of Tcl that uses the same CoW reference-counting trick. (Tcl is an under-appreciated FP language: everything is a string, and strings are immutable, so it has had efficient purely declarative data structures for decades).

The downside in Tcl is that if you refactor some code suddenly you can add a new reference and drop into accidentally quadratic territory because now everything is being copied. This leads to some cute/ugly hacks that are only explainable in terms of interpreter implementation details, purely to reduce a refcount in the right spot.

1 comments

In Swift you occasionally have to introduce a temporary local variable to avoid accidentally quadratic behavior, but I've never seen it require anything complicated or hard to explain.
In Tcl there is an idiom to do things like

    some_func $value[set value “”]
where the [set value “”] bit reduces the refcount. There’s also a fairly widespread idiom of using the K combinator for this[1]:

    some_func [K $value [set value “”]]
It’s one of those things that has become second-nature to people in-the-know, but is a total headscratcher otherwise.

[1]: https://wiki.tcl-lang.org/page/K#c2a6014c2d129837889d8a8000d...

Curious about an example of this in Swift? Is it a variable holding a collection like an Array?
A really simple example:

    class Foo {
        var foo = [Int]()
        var bar: [Int] {
            get {
                foo
            }
            set {
                foo = newValue
            }
        }
    }
    let obj = Foo()
Calling `obj.foo.append(i)` in a loop takes linear time, while `obj.bar.append(1)` is quadratic time. `obj.foo.append()` does a borrow operation resulting in there never being more than one reference at a time, while `obj.bar.append()` does a get followed by a set, meaning that there's always two references to the array and every append does a copy on write. `let bar = obj.bar; for i = 0..<max { bar.append(i) }; obj.bar = bar` would do just a single CoW.

Usually of course your computed properties actually do something so this difference feels less surprising. Swift does offer the undocumented `_modify` property accessor to let computed operations do borrows, but making it an official feature is waiting for noncopyable types to be finalized.