> On Dec 21, 2016, at 4:34 AM, Jeremy Pereira <jeremy.j.pere...@googlemail.com> > wrote: > >> >> On 20 Dec 2016, at 13:10, Matthew Johnson <matt...@anandabits.com> wrote: >> >> >> >> Sent from my iPad >> >>> On Dec 20, 2016, at 4:32 AM, Jeremy Pereira via swift-evolution >>> <swift-evolution@swift.org> wrote: >>> >>> >>>> On 20 Dec 2016, at 07:54, Pierre Monod-Broca via swift-evolution >>>> <swift-evolution@swift.org> wrote: >>>> >>>> But for a struct to be immutable, you don't need all its properties to be >>>> let. (I guess that's Derrick's point) >>> >>> Yes you do. Consider >>> >>> struct Person: Hashable >>> { >>> let firstName: String >>> let lastName: String >>> let hashValue: Int >>> >>> init(firstName: String, lastName: String) >>> { >>> self.firstName = firstName >>> self.lastName = lastName >>> self.hashValue = firstName.hashValue ^ lastName.hashValue >>> } >>> } >>> >>> func == (l: Person, r: Person) -> Bool >>> { >>> return l.firstName == r.firstName && l.lastName == r.lastName >>> } >>> >>> Pretend that the hash value is quite expensive to calculate so I only want >>> to do it once. With the above code, this is fine but if I change the lets >>> to vars (e.g. var firstName: String), I am open to >>> >>> let chris = Person(firstName: “Chris”, lastName: “Lattner”) // Immutable >>> >>> var andy = chris >>> andy.firstName = “Andy” >>> >>> andy.hashValue // Gives the wrong answer unless you are exceptionally lucky. >>> >>> I’ve used hashValue, but the same argument would apply to any computed >>> property where you might want to cache the computed value. As soon as you >>> make any of the properties that the computed property depends on `var`, you >>> have to add code that invalidates the cached value which is a performance >>> and a complexity hit for your struct. >> >> The performance hit is likely a bit larger if you *don't* use a mutable >> property and instead create a whole new instance. > > How is > > let a = SomeStruct() > var b = a > > not creating a new instance?
Of course this creates a new instance. But it’s not what I was talking about. What I was distinguishing is the performance characteristics of pure functional code that simply modifies one or more properties like this: let x = Person(firstName: “John”, lastName: “Black”) // don’t pay attention to syntax - any syntax with the same semantics // would have the same performance characteristics let y = Person(firstName: x.firstName, lastName: “White”) Relative to code that leverages the mutability model of Swift’s value types: var x = Person(firstName: “John”, lastName: “Black”) x.lastName = “White" The former is usually going to be slower because it requires copying, additional storage, and re-runs the entire initializer despite only modifying a single property. Sometimes the optimizer may be able to eliminate the difference, but that is not always going to be the case. Your observation that a lot of boilerplate is required if you need to cache a derived property is very fair. That is a legitimate problem that would be reasonable to solve. > > Anyway, the cost depends on how expensive the calculation for the calculated > property is and how often you use it and how well the compiler can optimise > copies of immutable objects. > > On the other hand, making a property that is not supposed to change over the > lifetime of the object a let property is self documenting and not to be > avoided IMO. This is perfectly fair. But if you need the ability to construct a nearly identical instance with only one, or maybe a few, modified properties you probably have code that would benefit from using Swift’s model for mutability of value types. > >> >> It might be interesting to think about language solutions to reduce this >> complexity. But in general, the mutability model of Swift's value types is >> an asset and should be embraced, not avoided. That's what a "Swifty" >> solution would do IMO. > > Yeah, I really hate it when people say “x is Swifty” or “y is not Swifty”. > What is Swifty or not usually depends on what the person saying it prefers. > On the other hand, most programmers i have come across agree that writing > code that is self documenting is good practice and therefore using let > instead of var for properties that never change over the life time of the > object counts in that respect in my opinion. “Swifty” was followed by IMO so it was clearly a statement of my personal opinion. I agree that self documenting code is a great practice. On the other hand there isn’t an increase in understanding of the semantics of a struct property by using `let` instead of `var` if that property will often be modified via an initializer that effectively creates a copy of a previous instance, but with a new value for said property. The differences between `let` and `var` in this case are pretty subtle and not usually consequential. There are certainly times when using let rather than var for members of a struct makes sense. An identifier is a great example where let is usually appropriate. But in *my* opinion there is a widespread misunderstanding of the semantic difference between let and var for structs. To demonstrate, in the following example both implementations of `updateOwner` have identical semantics: class Foo { var owner: Person // pure functional style func updateOwner(newFirstName: String) { owner = Person(firstName: newFirstName, lastName: owner.lastName) } // using Swift’s model for value type mutation, obviously only works if `firstName` is declared with `var` func updateOwner(newFirstName: String) { owner.firstName = newFirstName } } If your type is intended to be used in code with semantics like this there is no significant benefit to the pure functional style. Today there is a price in terms of syntax which could be solved provided sufficient motivation. There is also a price in terms of performance that may sometimes be optimized away, but not always. I’m not necessarily suggesting that one style or the other should always be preferred. What I am suggesting is that we should be clear about understanding what the tradeoffs are. And I am also suggesting that there are enough benefits to the mutability model for value types that identifying challenges with writing code that relies on this model (such as caching a computed property) is a more fruitful avenue for improving the language than offering syntactic sugar for more functional, perhaps lens-y style code. That’s not to say we can’t do both, but we do need to prioritize. > > >>> >>> _______________________________________________ >>> swift-evolution mailing list >>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>> https://lists.swift.org/mailman/listinfo/swift-evolution >>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution