> On May 15, 2016, at 2:01 PM, Dave Abrahams <[email protected]> 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 <[email protected]> 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
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution