<div dir="ltr">Xiaodi's point is really important—being able to express the notions simultaneously that "T has method a()" and "T does not have method a()" would break the type system.<div><br></div><div>Instead of focusing on the proposed syntax, let's consider the problem you're trying to solve. It sounds like what you're asking for could be expressed more cleanly with a richer protocol algebra that supported subtraction. It wouldn't be quite as automatic as what you propose, but it would feel like a more natural extension of the type system if you could do something like below, and would avoid combinatorial explosion of protocol types (you go from O(n!) to O(n) things you actually have to define concretely):</div><div><br></div><div>```</div><div>protocol WidthConstrainable {</div><div> func width(_ v: CGFloat) -> Self – WidthConstrainable</div><div>}</div><div>protocol HeightConstrainable {</div><div> func height(_ v: CGFloat) -> Self – HeightConstrainable</div><div>}</div><div>protocol XConstrainable {</div><div> func x(_ v: CGFloat) -> Self – XConstrainable</div><div>}</div><div>protocol YConstrainable {</div><div> func y(_ v: CGFloat) -> Self – YConstrainable</div><div>}</div><div>struct Constraint: WidthConstrainable, HeightConstrainable, XConstrainable, YConstrainable {</div><div> ...</div><div>}</div><div>```</div><div><br></div><div>If a type X is just a union or protocols (for example, X: WidthConstrainable & HeightConstrainable & XConstrainable & YConstrainable), the subtraction (X – something) is easy to define. It's either valid if the subtrahend is present in the set, or it's invalid (and detectable at compile time) if it's not.</div><div><br></div><div>But there are still some rough edges: what does it mean when a concrete type is involved? Let's say you have T: P1 & P2 & P3, and you write (T – P1). That could give you a type that contains all the members of T except those in P1, which would be the members in P2, P3, and any that are defined directly on T that do not come from protocol conformances.</div><div><br></div><div>But what is the relationship between types T and (T – P1)? (T – P1) being a supertype of T seems fairly straightforward—any instance of T can be expressed as (T – P1). But if I have an instance of type (T – P1), should I be able to cast that back to T? On the one hand, why not? I can obviously only get (T – P1) by starting with T at some point, so any instance of (T – P1) must *also* be an instance of T. So that implies that T is a supertype of (T – P1). In other words, they're supertypes of each other, without being the same type? That would be a first in Swift's type system, I believe. And if we allow the cast previously mentioned, that effectively circumvents the goal you're trying to achieve. (We could argue that you'd have to use a force-cast (as!) in this case.)</div><div><br></div><div>This could be worked around by forbidding subtraction from concrete types and reducing T to the union of its protocols before performing the subtraction. In that case, (T – P1) would equal P2 & P3. But that relationship is still a little wonky: in that case, (T – P1) would also not contain any members that are only defined on T, even though the expression (T – P1) implies that they should. You would have to make that reduction explicit somehow in order for that to not surprise users (require writing something like `#protocols(of: T) – P1`?), and it leaves a certain subset of possible type expressions (anything that wants members defined on T without members of a protocol) unexpressible.</div><div><br></div><div>I actually glossed over this earlier by writing "..." in the struct body. If I defined `width(_:)` there, what would my return type be? We currently forbid `Self` in that context. Would `Self – WidthConstrainable` be allowed? Would I have to use the new protocol-reduction operator above? More details that would have to be worked out.</div><div><br></div><div>Protocol inheritance would pose similar questions. If you have this:</div><div><br></div><div>```</div><div>protocol P1 {}</div><div>protocol P2: P1 {}</div><div>```</div><div><br></div><div>What is the subtype/supertype relationship between P2 and (P2 – P1)? It's the same situation we had with a concrete type. Maybe you just can't subtract a super-protocol without also subtracting its lowest sub-protocol from the type?</div><div><br></div><div>My PL type theory knowledge isn't the deepest by any means, but if something like this was workable, I think it would be more feasible and more expressive than the member permutation approach. And that being said, this is probably a fairly narrow use case that wouldn't warrant the complexity it would bring to the type system to make it work.</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr">On Mon, Dec 26, 2016 at 7:03 AM Xiaodi Wu via swift-evolution <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Should the following compile?<br class="gmail_msg"><br class="gmail_msg">let bar = foo.a()<br class="gmail_msg">func f(_ g: T) {<br class="gmail_msg"> _ = g.a()<br class="gmail_msg">}<br class="gmail_msg">f(bar)<br class="gmail_msg"><br class="gmail_msg">If so, your proposal cannot guarantee each method is called only once. If not, how can bar be of type T?<br class="gmail_msg"><br class="gmail_msg"><div class="gmail_quote gmail_msg"><div dir="ltr" class="gmail_msg">On Mon, Dec 26, 2016 at 06:30 Adrian Zubarev <<a href="mailto:adrian.zubarev@devandartist.com" class="gmail_msg" target="_blank">adrian.zubarev@devandartist.com</a>> wrote:<br class="gmail_msg"></div><blockquote class="gmail_quote gmail_msg" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word" class="gmail_msg"><div class="m_-3067540519206532711m_2486499801731300355bloop_markdown gmail_msg"><p class="gmail_msg">I think I revise what I said about value semantics in my last post.</p>
<pre class="gmail_msg"><code class="m_-3067540519206532711m_2486499801731300355swift gmail_msg">let chain: T = foo.a()
let new = chain
new. // should not see `a` here
</code></pre>
<p class="gmail_msg">It’s more something like a local scoped chain. I’m not sure how to call it correctly here. I’m not a native English speaker. =)</p>
<p class="gmail_msg"></p></div><div class="m_-3067540519206532711m_2486499801731300355bloop_original_html gmail_msg"></div></div><div style="word-wrap:break-word" class="gmail_msg"><div class="m_-3067540519206532711m_2486499801731300355bloop_original_html gmail_msg"><div id="m_-3067540519206532711m_2486499801731300355bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px;color:rgba(0,0,0,1.0);margin:0px;line-height:auto" class="gmail_msg"><br class="gmail_msg"></div> <br class="gmail_msg"> <div id="m_-3067540519206532711m_2486499801731300355bloop_sign_1482751593527631872" class="m_-3067540519206532711m_2486499801731300355bloop_sign gmail_msg"><div style="font-family:helvetica,arial;font-size:13px" class="gmail_msg">-- <br class="gmail_msg">Adrian Zubarev<br class="gmail_msg">Sent with Airmail</div></div> <br class="gmail_msg"></div></div><div style="word-wrap:break-word" class="gmail_msg"><div class="m_-3067540519206532711m_2486499801731300355bloop_original_html gmail_msg"><p class="m_-3067540519206532711m_2486499801731300355airmail_on gmail_msg">Am 26. Dezember 2016 um 12:11:23, Adrian Zubarev (<a href="mailto:adrian.zubarev@devandartist.com" class="gmail_msg" target="_blank">adrian.zubarev@devandartist.com</a>) schrieb:</p> <blockquote type="cite" class="m_-3067540519206532711m_2486499801731300355clean_bq gmail_msg"><span class="gmail_msg"><div style="word-wrap:break-word" class="gmail_msg"><div class="gmail_msg"></div><div class="gmail_msg">
<div class="m_-3067540519206532711m_2486499801731300355bloop_markdown gmail_msg">
<p class="gmail_msg">By ‘calling once’ I meant, calling once at a single permutation
chain. If the chain is escaped or followed by a non-permuting
member that returns the same protocol, you’d have the ability to
use all members at the starting point of the new chain.</p>
<pre class="gmail_msg"><code class="m_-3067540519206532711m_2486499801731300355swift gmail_msg">permuting protocol T {
func a()
func b()
func c()
func d()
}
var foo: T = …
func boo(_ val: T) -> U {
// Here val escapes the chain and creates a new one
// That means that you can create a local permutation chain here again
val.a() // we can use `a` here
return …
}
boo(foo.a()) // a is immediately invoked here
</code></pre>
<p class="gmail_msg">I imagine this keyword to follow value semantics, so that any
possible mutation is handled locally with a nice extra ability of
permutation member chaining.</p>
<p class="gmail_msg">Did I understood your point correctly here?</p>
<hr class="gmail_msg">
<p class="gmail_msg">Sure the idea needs to be more fleshed out, but I’m curious if
that’s something that we might see in Swift one day. :)</p>
</div>
<div class="m_-3067540519206532711m_2486499801731300355bloop_original_html gmail_msg">
<div id="m_-3067540519206532711m_2486499801731300355bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px;color:rgba(0,0,0,1.0);margin:0px;line-height:auto" class="gmail_msg">
<br class="gmail_msg"></div>
<br class="gmail_msg">
<div id="m_-3067540519206532711m_2486499801731300355bloop_sign_1482749838336907008" class="m_-3067540519206532711m_2486499801731300355bloop_sign gmail_msg">
<div style="font-family:helvetica,arial;font-size:13px" class="gmail_msg">
-- <br class="gmail_msg">
Adrian Zubarev<br class="gmail_msg">
Sent with Airmail</div>
</div>
<br class="gmail_msg">
<p class="m_-3067540519206532711m_2486499801731300355airmail_on gmail_msg">Am 26. Dezember 2016 um 11:50:50, Xiaodi Wu
(<a href="mailto:xiaodi.wu@gmail.com" class="gmail_msg" target="_blank">xiaodi.wu@gmail.com</a>)
schrieb:</p>
<blockquote type="cite" class="m_-3067540519206532711m_2486499801731300355clean_bq gmail_msg">
<div class="gmail_msg"><span class="gmail_msg"><span style="color:rgb(0,0,0);font-family:'helvetica Neue',helvetica;font-size:14px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);display:inline!important;float:none" class="gmail_msg">
Given `foo: T` and methods a(), b(), c(), d(), each of which can
only be called once, how can the return value of these methods be
represented in the type system?</span><br style="color:rgb(0,0,0);font-family:'helvetica Neue',helvetica;font-size:14px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="gmail_msg">
<br style="color:rgb(0,0,0);font-family:'helvetica Neue',helvetica;font-size:14px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="gmail_msg">
<span style="color:rgb(0,0,0);font-family:'helvetica Neue',helvetica;font-size:14px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);display:inline!important;float:none" class="gmail_msg">
That is, if `foo.a()` can be passed as an argument to an arbitrary
function of type `(T) -> U`, either the function cannot
immediately invoke a(), in which case foo is not of type T, or it
can immediately invoke a(), in which case your keyword does not
work.</span></span></div>
</blockquote>
</div>
<div class="m_-3067540519206532711m_2486499801731300355bloop_markdown gmail_msg"></div>
</div></div></span></blockquote></div></div></blockquote></div>
_______________________________________________<br class="gmail_msg">
swift-evolution mailing list<br class="gmail_msg">
<a href="mailto:swift-evolution@swift.org" class="gmail_msg" target="_blank">swift-evolution@swift.org</a><br class="gmail_msg">
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" class="gmail_msg" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class="gmail_msg">
</blockquote></div>