<html><head><style>
body {
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
        padding:1em;
        margin:auto;
        background:#fefefe;
}
h1, h2, h3, h4, h5, h6 {
        font-weight: bold;
}
h1 {
        color: #000000;
        font-size: 28pt;
}
h2 {
        border-bottom: 1px solid #CCCCCC;
        color: #000000;
        font-size: 24px;
}
h3 {
        font-size: 18px;
}
h4 {
        font-size: 16px;
}
h5 {
        font-size: 14px;
}
h6 {
        color: #777777;
        background-color: inherit;
        font-size: 14px;
}
hr {
        height: 0.2em;
        border: 0;
        color: #CCCCCC;
        background-color: #CCCCCC;
display: inherit;
}
p, blockquote, ul, ol, dl, li, table, pre {
        margin: 15px 0;
}
a, a:visited {
        color: #4183C4;
        background-color: inherit;
        text-decoration: none;
}
#message {
        border-radius: 6px;
        border: 1px solid #ccc;
        display:block;
        width:100%;
        height:60px;
        margin:6px 0px;
}
button, #ws {
        font-size: 12 pt;
        padding: 4px 6px;
        border-radius: 5px;
        border: 1px solid #bbb;
        background-color: #eee;
}
code, pre, #ws, #message {
        font-family: Monaco;
        font-size: 10pt;
        border-radius: 3px;
        background-color: #F8F8F8;
        color: inherit;
}
code {
        border: 1px solid #EAEAEA;
        margin: 0 2px;
        padding: 0 5px;
}
pre {
        border: 1px solid #CCCCCC;
        overflow: auto;
        padding: 4px 8px;
}
pre > code {
        border: 0;
        margin: 0;
        padding: 0;
}
#ws { background-color: #f8f8f8; }
.bloop_markdown table {
border-collapse: collapse;
font-family: Helvetica, arial, freesans, clean, sans-serif;
color: rgb(51, 51, 51);
font-size: 15px; line-height: 25px;
padding: 0; }
.bloop_markdown table tr {
border-top: 1px solid #cccccc;
background-color: white;
margin: 0;
padding: 0; }
.bloop_markdown table tr:nth-child(2n) {
background-color: #f8f8f8; }
.bloop_markdown table tr th {
font-weight: bold;
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
.bloop_markdown table tr td {
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
.bloop_markdown table tr th :first-child, table tr td :first-child {
margin-top: 0; }
.bloop_markdown table tr th :last-child, table tr td :last-child {
margin-bottom: 0; }
.bloop_markdown blockquote{
border-left: 4px solid #dddddd;
padding: 0 15px;
color: #777777; }
blockquote > :first-child {
margin-top: 0; }
blockquote > :last-child {
margin-bottom: 0; }
code, pre, #ws, #message {
word-break: normal;
word-wrap: normal;
}
hr {
display: inherit;
}
.bloop_markdown :first-child {
-webkit-margin-before: 0;
}
code, pre, #ws, #message {
font-family: Menlo, Consolas, Liberation Mono, Courier, monospace;
}
.send { color:#77bb77; }
.server { color:#7799bb; }
.error { color:#AA0000; }</style></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class="bloop_markdown"><p>Okay now I see your point there. :) Thank you Xiaodi and Tony.</p>
<hr>
<p>It’s an interesting approach you have there, but I see another problem with <code>Self</code> and <code>- ProtocolName</code>. <code>Self</code> does not refer to the current type returned from the protocol member. SE–0068 might help there, but as soon we’re working with non-final classes it will be problematic again.</p>
<p>That means something like this will be possible <code>constraint.x(1).y(1).x(2)</code>, which by solving the main problem of this topic we’d like to avoid.</p>
<p>We’d need a way to subtract a protocol from the returned type + the ability of keeping <code>T</code> only members.</p>
<p>As for <code>T : P1 & P2 & P3</code>, <code>T - P1</code> should return <code>T + P2 & P3</code>, because it’s what the user would logically assume there. The next chain needs to remember <code>- P1</code> on that path, so the result for the followed reduction of <code>- P3</code> would be equivalent to <code>T - P1 & P3</code> = <code>T + P2</code>.</p>
<p>As you already mentioned, one would assume that we might be able to cast back to <code>T</code> from <code>T - P1 & P3</code>. I think this leads us to the right direction where we should realize that we should escape from <code>T</code> in general. That means that the return type should somehow create a new subtraction type, which can be reduced further by member chaining or escaped similar to what I wrote in the original post.</p>
<p>We’d need a new type or keyword that refers to the current (reduced) type. Let’s call it <code>Current</code> instead of <code>Self</code>. Furthermore we’d need a concrete result type, to be able to pass the result value around. Let’s call the latter type <code>Subtraction<T></code>.</p>
<pre><code class="swift">protocol WidthConstrainable {
func width(_ v: CGFloat) -> Subtraction<Current – WidthConstrainable>
}
protocol HeightConstrainable {
func height(_ v: CGFloat) -> Subtraction<Current – HeightConstrainable>
}
protocol XConstrainable {
func x(_ v: CGFloat) -> Subtraction<Current – XConstrainable>
}
protocol YConstrainable {
func y(_ v: CGFloat) -> Subtraction<Current – YConstrainable>
}
struct Constraint: WidthConstrainable, HeightConstrainable, XConstrainable, YConstrainable {
...
}
</code></pre>
<p><code>Subtraction<T></code> could be a similar type, like we’re proposing to change the metatypes from <code>T.Type/Protocol</code> to <code>Type<T></code> and <code>AnyType<T></code>. That means that it would know all the members of <code>Current</code> without the subtrahend. Each further member chain would create ether a nested <code>Subtraction<Subtraction<T - P1> - P2></code> or a flattened type like <code>Subtraction<T - P1 & P2></code>.</p>
<pre><code class="swift">let constraint = Constraint()
let a: Subtraction<Constraint - XConstrainable> = constraint.x(1)
let b: Subtraction<Subtraction<Constraint - XConstrainable> - YConstrainable> = a.y(2)
let c: Subtraction<Subtraction<Subtraction<Constraint - XConstrainable> - YConstrainable> - WidthConstrainable> = b.width(100)
_ = constraint.x(1).y(2).width(100)
</code></pre>
<p>That way we could even remove the new <code>-</code> operator and use generics parameter list instead. Maybe instead of <code>Subtraction</code> we could call the new type <code>Difference<T></code></p>
<pre><code class="swift">type Difference<Minuend, Subtrahend> : Minuend - Subtrahend { … } // should know everything `Minuend` has, but exclude everything from `Subtrahend`
struct Constraint: WidthConstrainable, HeightConstrainable, XConstrainable, YConstrainable {
func width(_ v: CGFloat) -> Difference<Current, WidthConstrainable> {
var copy = self
copy.width = v
return Difference(copy)
}
func height(_ v: CGFloat) -> Difference<Current, HeightConstrainable> { … }
func x(_ v: CGFloat) -> Difference<Current, XConstrainable> { … }
func y(_ v: CGFloat) -> Difference<Current, YConstrainable> { … }
}
</code></pre>
<p>What do you guys think about this approach? :)</p>
<p></p></div><div class="bloop_original_html"><style>body{font-family:Helvetica,Arial;font-size:13px}</style><div id="bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px; color: rgba(0,0,0,1.0); margin: 0px; line-height: auto;"><br></div> <br> <div id="bloop_sign_1482828564757652992" class="bloop_sign"><div style="font-family:helvetica,arial;font-size:13px">-- <br>Adrian Zubarev<br>Sent with Airmail</div></div> <br><p class="airmail_on">Am 26. Dezember 2016 um 17:17:29, Tony Allevato (<a href="mailto:allevato@google.com">allevato@google.com</a>) schrieb:</p> <blockquote type="cite" class="clean_bq"><span><div><div></div><div>
<title></title>
<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">
<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"><span 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.</span></p>
<pre class="gmail_msg"><span 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></span></pre>
<p class="gmail_msg"><span 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.</span></p>
<p class="gmail_msg"><span class="gmail_msg">Did I understood your
point correctly here?</span></p>
<hr class="gmail_msg">
<p class="gmail_msg"><span 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. :)</span></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"><span class="gmail_msg"><br class="gmail_msg"></span></div>
<span class="gmail_msg"><br class="gmail_msg"></span>
<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"><span class="gmail_msg">-- <br class="gmail_msg">
Adrian Zubarev<br class="gmail_msg">
Sent with Airmail</span></div>
</div>
<span class="gmail_msg"><br class="gmail_msg"></span>
<p class="m_-3067540519206532711m_2486499801731300355airmail_on gmail_msg">
<span class="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:</span></p>
<blockquote type="cite" class="m_-3067540519206532711m_2486499801731300355clean_bq gmail_msg">
<div class="gmail_msg"><span 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></span></div>
</blockquote>
</div>
<div class="m_-3067540519206532711m_2486499801731300355bloop_markdown gmail_msg">
</div>
</div>
</div>
</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>
</div></div></span></blockquote></div><div class="bloop_markdown"><p></p></div></body></html>