> On Dec 21, 2016, at 4:34 AM, Jeremy Pereira <[email protected]>
> wrote:
>
>>
>> On 20 Dec 2016, at 13:10, Matthew Johnson <[email protected]> wrote:
>>
>>
>>
>> Sent from my iPad
>>
>>> On Dec 20, 2016, at 4:32 AM, Jeremy Pereira via swift-evolution
>>> <[email protected]> wrote:
>>>
>>>
>>>> On 20 Dec 2016, at 07:54, Pierre Monod-Broca via swift-evolution
>>>> <[email protected]> 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
>>> [email protected] <mailto:[email protected]>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution