> 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

Reply via email to