> On May 15, 2016, at 2:01 PM, Dave Abrahams <dabrah...@apple.com> wrote:
> 
> 
> on Fri May 13 2016, Matthew Johnson <matthew-AT-anandabits.com 
> <http://matthew-at-anandabits.com/>> wrote:
> 
>> Sent from my iPad
>> 
>>> On May 13, 2016, at 9:12 AM, Dave Abrahams <dabrah...@apple.com> wrote:
>>> 
>>> 
>>>> on Mon May 09 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:
>>>> 
>>>>   My claim is that substituting the constraint of “it has value
>>>>   semantics,” while presumably looser than the PureValue constraint, would
>>>>   not compromise the correctness of your view controller, so not only is
>>>>   the meaning of PureValue hard to define, but it doesn't buy you
>>>>   anything.  If you want to refute that, just show me the code.
>>>> 
>>>>       This is not an algorithmic use but is still perfectly valid IMO.
>>>> 
>>>>   If the properties of PureValue matter to your view controller, there's
>>>>   an algorithm somewhere that depends on those properties for its
>>>>   correctness.
>>>> 
>>>> In many cases it may just be view configuration that depends on those
>>>> properties.  I suppose you can call view configuration code an
>>>> algorithm but I think that would fall outside of common usage.
>>> 
>>> It's an algorithm, or if the configuration is declarative, there's an
>>> algorithm that manipulates it.  That said, I still don't have a concrete
>>> example of how view configuration can depend on these properties.
>> 
>> The algorithm might just be "copy x bit of data to y view property,
>> etc".  That is so trivial that it feels like a stretch to call it an
>> algorithm.
> 
> Algorithms can be trivial.

Fair enough.  Although in most contexts people don’t use the word when 
discussing the trivial.

> 
>> That "algorithm" doesn't depend on this property because it executes
>> at a single point in time.  However, a view controller might depend on
>> that property in order to render properly across time (for example,
>> configuring cells as they scroll on and off screen).
> 
> The example is too abstract for me to understand.
> 
> Let me put this differently: I recognize that your concept of
> “PureValue” may be a *sufficient* condition for some generic
> algorithm/component to work, but it is never a *necessary* condition,
> because genericity (or use of a superclass or protocol type) erases
> details of the actual types involved from the point of view of the
> algorithm/component.  It doesn't matter if your type contains a class
> reference if it has value semantic properties.  My claim is that
> PureValue is an overly-restrictive constraint that makes many things
> less useful than they should be.

In many cases this is true - you don’t need more than value semantics as you 
define it.  However it is not at all true that PureValue is never necessary for 
the correctness of code.  I’m going to provide an example to the contrary below.

> 
>> This property allows us to separate values from non-local mutation and
>> make such separation a requirement.  Rather than observing mutations
>> of objects with KVO, etc we might prefer to observe something that
>> provides a new aggregate value instead, while requiring the entire
>> aggregate value itself to be (observably) immutable at all times
>> (because we stored it with a let property locally).  This can make it
>> easier to reason about correct behavior of your code.  But it doesn't
>> work unless all visible parts of the aggregate are immutable.
>> 
>> If you're not familiar with Elm, Redux, etc it might be worth taking
>> a look.  
> 
> That's a pretty broad link.  At which parts do you think I should look?

The piece that matters here is state management.  The core concept is to 
tightly control how mutations happen.  It is modeled in terms of state type T, 
an initial value t, an action type A (instances of which are mutation commands, 
as in the command pattern), and a reducer function (T, A) -> T which produces a 
new state.

Here’s a toy implementation that is somewhat simplistic but captures the 
essence of the concept:

class Store<State, Action> {
    typealias Reducer = (State, Action) -> State
    
    var stateHistory: [State]
    let reducer: Reducer
    
    init(initialState: State, reducer: Reducer) {
        stateHistory = [initialState]
        self.reducer = reducer
    }
    
    func applyAction(action: Action) {
        let newState = reducer(stateHistory.last!, action)
        stateHistory.append(newState)
    }
    
    var currentState: State {
        return stateHistory.last!
    }
    
    var canUndo: Bool {
        return stateHistory.count > 1
    }
    
    func undo() {
        if canUndo {
            stateHistory.popLast()
        }
    }
}

This design relies on State being a PureValue.  The whole idea is that the only 
way any path of observation rooted at currentState can change is when the 
reducer returns a new state when it is called by `applyAction`.  That guarantee 
cannot be provided by value semantics alone under your definition of value 
semantics.  Further, each state in the history is intended to be a static 
snapshot of the “currentState” state at a specific point in time.  All states 
should be logically independent from each other and from anything anywhere else 
in the program.  This cannot be guaranteed under your definition of value 
semantics.  

If we allow State to be Array<MyMutableReferenceType> which has value semantics 
under your definition it is clear that we should have no expectation that the 
desired properties are preserved.  The Store class is fundamentally broken if 
it can be used with State types that are not pure values.

I’m not sure why it didn’t occur to me sooner, but this strategy for managing 
app state is very similar in nature to what Sean Parent discusses in his value 
semantics talk (https://www.youtube.com/watch?v=_BpMYeUFXv8 
<https://www.youtube.com/watch?v=_BpMYeUFXv8> and related 
https://www.youtube.com/watch?v=bIhUE5uUFOA 
<https://www.youtube.com/watch?v=bIhUE5uUFOA>). 

Sean discusses using value semantics to model applications state (he calls it 
document).  His examples don’t use reified actions and reducer functions so it 
is a bit different but it relies on the same pure value semantics.  The demo he 
gives in the talk is a toy example modeled on the design Photoshop uses to 
implement its history feature.  This design relies on each document in the 
history being an aggregate which is a PureValue.  This is not an academic 
discussion, but on with real world practical utility.

Sean says “value semantics is similar to functional programming, except objects 
still have addresses and in-situ operations…  You're trying to maintain the 
ability to locally reason about your code but you're not denying the fact that 
the machine has memory and you can do in-situ operations on it”.  Towards the 
end he quotes from a discussion he had with John Backus (inventor of FP).  John 
said: “it always annoyed me that FP and the no side effect way of programming 
had become a religion.  With FP I was trying to come up with a mathematically 
rigorous way to program and I understood the complexities of having side 
effects.  I always knew we had to deal with side effects, I just wanted a 
structured way to do it.”  John agreed that Sean’s approach to value semantics 
provides such a structure.  Allowing shared mutable references throws away that 
structured approach.  (As an aside, this is the most exciting thing about value 
semantics IMO - it provides a structured approach to side effects, allowing 
local reasoning about code).  

Sean gives an example of how references break the ability to reason locally 
where he has two shared_ptrs that point to the same object:

"If you look at it in terms of just the individual types you kind of do have 
value semantics.  When I copy a shared pointer it copies the pointer with value 
semantic operations...  The problem is the references.  When I'm looking at a 
shared_ptr I'm looking at it as if I have the object.  So really what I have is 
two objects that intersect.  So really my object in the program is this whole 
connected mess.  At any particular point in code I have difficulty reasoning 
about the whole thing.  The shared structure breaks our ability to reason 
locally about code."

Sean makes an important distinction between looking at individual types and 
looking at the aggregate as a whole.  It is very important to him that the 
entire aggregate be logically independent as this facilitates local reasoning.  
This is exactly what I have been calling a pure value.  Pure value never allows 
any intersection to be observed (immutable intersections are allowed because 
they cannot be observed, which Sean alludes to in passing).  Incidentally, it 
is pretty clear from the talk that immutable intersection is heavily used in 
Photoshop history in order to keep memory use reasonable.  This falls into the 
category of persistent data structures.

My impression is that Sean’s definition of "value semantics” excludes 
“intersecting objects” (where the intersection is mutable) and is aligned with 
John’s “full value semantics” and the notion of “pure value” we have been 
discussing.  

-Matthew




> 
> -- 
> -Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to