on Wed Aug 23 2017, Joe Groff
<[email protected]> wrote:
On Aug 18, 2017, at 12:10 PM, Chris Lattner via swift-evolution
<[email protected]> wrote: Splitting this out from the
concurrency thread:
On Aug 18, 2017, at 6:12 AM, Matthew Johnson
<[email protected]> wrote:
On Aug 17, 2017, at 11:53 PM, Chris Lattner
<[email protected]> wrote:
In the manifesto you talk about restrictions on passing
functions across an actor message. You didn’t discuss pure
functions, presumably because Swift doesn’t have them yet.
I imagine that if (hopefully when) Swift has compiler
support for verifying pure functions these would also be
safe to pass across an actor message. Is that correct?
Correct. The proposal is specifically/intentionally
designed to be light on type system additions, but there are
many that could make it better in various ways. The logic
for this approach is that I expect *a lot* of people will be
writing mostly straight-forward concurrent code, and that
goal is harmed by presenting significant type system hurdles
for them to jump over, because that implies a higher learning
curve. This is why the proposal doesn’t focus on a provably
memory safe system: If someone slaps “ValueSemantical” on a
type that doesn’t obey, they will break the invariants of the
system. There are lots of ways to solve that problem
(e.g. the capabilities system in Pony) but it introduces a
steep learning curve. I haven’t thought a lot about
practically getting pure functions into Swift, because it
wasn’t clear what problems it would solve (which couldn’t be
solved another way). You’re right though that this could be
an interesting motivator.
I can provide a concrete example of why this is definitely
and important motivator. My current project uses pure
functions, value semantics and declarative effects at the
application level and moves as much of the imperative code as
possible (including effect handling) into library level code.
This is working out really well and I plan to continue with
this approach. The library level code needs the ability to
schedule user code in the appropriate context. There will
likely be some declarative ability for application level code
to influence the context, priority, etc, but it is the library
that will be moving the functions to the final context. They
are obviously not closure literals from the perspective of the
library. Pure functions are obviously important to the
semantics of this approach. We can get by without compiler
verification, using documentation just as we do for protocol
requirements that can't be verified. That said, it would be
pretty disappointing to have to avoid using actors in the
implementation simply because we can't move pure functions
from one actor to another as necessary. To be clear, I am
talking in the context of "the fullness of time". It would be
perfectly acceptable to ship actors before pure functions.
That said, I do think it's crucial that we eventually have the
ability to verify pure functions and move them around at will.
Right. Pure functions are also nice when you care about
thread safety, and there is a lot of work on this. C has
__attribute__((const)) and ((pure)) for example, c++ has
constexpr, and many research languages have built full blown
effects systems. My principle concern is that things like
this quickly become infectious: LOTS of things are pure
functions, and requiring them all to be marked as such becomes
a lot of boilerplate and conceptual overhead. This is
happening in the C++ community with constexpr for example.
The secondary concern is that you need to build out the model
enough that you don’t prevent abstractions. A pure function
should be able to create an instance of a struct, mutate it
(i.e. calling non-pure functions) etc. This requires a
non-trivial design, and as the design complexity creeps, you
run the risk of it getting out of control.
Now that inout parameters are guaranteed exclusive, a mutating
method on a struct or a function that takes inout parameters is
isomorphic to one that consumes the initial value as a pure
argument and returns the modified value back. This provides a
value-semantics-friendly notion of purity, where a function can
still be considered pure if the only thing it mutates is its
unescaped local state and its inout parameters and it doesn't
read or write any shared mutable state such as mutable globals,
instance properties, or escaped variables. That gives you the
ability to declare local variables and composably apply "pure"
mutating operations to them inside a pure function.
We've already brought Swift somewhat into the effects-system
design space with "throws" (and "async", if it gets taken as
we've currently proposed it), and we already have some
abstraction debt to pay off with "throws"; if we wanted to, we
could conceivably fold "impure" into that system as well. While
it's true that there would be a lot of effort in propagating
pure annotations to the right places, that's going to be true of
any attempt to move the current everything-goes world into a
more robust and constrained framework. I don't think we should
write off the notion completely.
I agree with Joe here... in principle. We keep finding features
we want to add that have effects-system-like semantics, and I
expect that we will continue to do so. The best language design
would probably result from addressing this category of feature in
some holistic way. My main concern is that in practice, it seems
unlikely to be doable before ABI lockdown makes it too late.
--
-Dave
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution