> On Dec 22, 2016, at 4:54 AM, Jeremy Pereira <jeremy.j.pere...@googlemail.com> 
> wrote:
> 
>> 
>> On 21 Dec 2016, at 14:07, Matthew Johnson <matt...@anandabits.com> wrote:
>> 
>>>> 
>>>> 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.
> 
> But those two pieces of code do not do the same thing. In the first case, you 
> still have the original value of x, in the second case you have “destroyed” 
> it. If, however, you had
> 
> var x = Person(firstName: “John”, lastName: “Black”)
> doSomethingWith(x)
> x.lastName = “White”
> doSomethingWith(x)
> 
> i.e. you are recycling x, I would agree it is (probably) faster, but to me it 
> screams badness and I would only consider doing it if I have evidence that 
> the natural immutable model has performance issues.

Yes, this is true.  However, this thread is about improving the convenience of 
immutable structs / functional code.  It is quite common in such code that the 
previous value is no longer needed.  I am encouraging people to focus on what 
the real benefits of this style is (value semantics) and how the same benefit 
might be achieved in a different way in a hybrid language like Swift.

> 
>> 
>> 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.
> 
> I’d rather have a model that accurately reflected the real World problem and 
> only compromise it if I have evidence that I had a performance problem and 
> this would help alleviate it.

I’m not arguing for premature optimization.  I am just saying that if you have 
two approaches with the same semantics and one has both syntactic and 
performance benefits it might be a good idea to consider using the one with the 
advantages.

> 
>> 
>>> 
>>>> 
>>>> 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. 
> 
> So why didn’t you just say “but in general, the mutability model of Swift's 
> value types is an asset and should be embraced, not avoided IMO”?
> 
>> 
>> 
>> 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.
> 
> If I were glancing at Foo and I saw the method signature `func 
> updateOwner(newFirstName: String)` I would think “oh, this is a function for 
> changing the first name of the owner” not “oh this is a method for changing 
> the owner to a different person who happens to have the same details except 
> the first name”. I would have no problem with making firstName and lastName 
> var in this case because we are trying to model a situation in which people 
> are allowed to change their names.
> 
> These two lines, in isolation 
>> owner = Person(firstName: newFirstName, lastName: owner.lastName)
> 
> and
>> owner.firstName = newFirstName
> 
> have different semantics as far as I am concerned. The first is assigning a 
> new owner. The second is the existing owner changing their name. I’d rather 
> not see the second form when a new owner is assigned and I’d rather not see 
> the first form if the owner has merely changed their name by deed poll. 

I would be curious to hear what Joe Groff and Dave Abrahams (or others on the 
core team) have to say about this.  My understanding is that the semantics are 
logically identical as long as the type preserves value semantics.  In both 
cases the value of the member is changed with the only difference being the 
modified first name.

> 
> When I am designing classes and structs, the only consideration I use for 
> whether something should be let or var is “can it legitimately change over 
> the lifetime of an instance”. If the answer is “no” it’s a let. I might 
> modify my decision in the light of profiling data, but if I did, the code 
> would be annotated with comments to explain why.

I agree with this and I gave an example of something that I think we can agree 
legitimately should not change: an identifier.  Where may disagree is in other 
details of “legitimate”.  

I am making that case that for the use cases discussed in this thread where the 
*intent* is to construct new values by modifying one or more properties of an 
existing value, Swift’s mutation model for value types is a powerful tool that 
is often going to be the right choice.  If you use “immutable” properties when 
you actually need to “mutate” them you are fighting the language without really 
getting any benefit in return.  IMO, it is worthwhile to take time to think 
through the semantics and reach an understanding of why this is the case.

One final comment - It’s important to remember that you can still create fully 
immutable *instances* of a struct even if it has `var` properties.  All you 
need to do is declare the instance `let`.

> 
>> 
>>> 
>>> 
>>>>> 
>>>>> _______________________________________________
>>>>> 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