<div dir="ltr"><div class="gmail_default" style="font-family:monospace"><span style="font-family:arial,sans-serif">On 8 April 2015 at 17:25, Andrew Barnert </span><span dir="ltr" style="font-family:arial,sans-serif"><<a href="mailto:abarnert@yahoo.com" target="_blank">abarnert@yahoo.com</a>></span><span style="font-family:arial,sans-serif"> wrote:</span><br></div><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="auto"><div><div><div>On Apr 7, 2015, at 22:12, Anthony Towns <<a href="mailto:aj@erisian.com.au" target="_blank">aj@erisian.com.au</a>> wrote:<span style="color:rgb(34,34,34)"> </span></div></div></div></div></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="auto"><div><div><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><div style="font-family:monospace"> case point is:<br></div><div style="font-family:monospace"> (x:=Number, y:=Number):<br> ...<br></div><div style="font-family:monospace"> {"x": x:=Number, "y": y:=Number}:<br> ...<br></div><div style="font-family:monospace"> x,y:=re_pt:<br> ...</div></div></div></div></div></div></blockquote></div></div><div>This is pretty much my proposal with different syntax. I don't think the := buys you anything, and it doesn't make it obvious how to have a pattern that recursively matches its parts. In my proposal, this would look like:</div></div></blockquote><div><br></div><div><div class="gmail_default" style="font-family:monospace">Hmm, I thought that was reasonably straightforward. You'd say things like:</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> case rect_coords is:</div><div class="gmail_default" style="font-family:monospace"> (pos := (left := Number, top := Number), size := (width := Number, height := Number)):</div><div class="gmail_default" style="font-family:monospace"> ...</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">to pull out rect_coords == (pos, size), pos == (left, top), size == (width, height).</div></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="auto"><span><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><div style="font-family:monospace">Though maybe it'd be interesting to have a "deconstructor" protocol for that instead, ie:</div><div style="font-family:monospace"><br></div><div style="font-family:monospace"> Foo(a,b,key=c) = foo</div><div style="font-family:monospace"><br></div><div style="font-family:monospace">is equivalent to something like:</div><div style="font-family:monospace"><br></div><div style="font-family:monospace"> x = Foo.__deconstruct__(foo)</div><div style="font-family:monospace"> a = x[0]</div><div style="font-family:monospace"> b = x[1]</div><div style="font-family:monospace"> c = x["key"]</div></div></div></div></div></div></blockquote><div><br></div></span><div>That is almost exactly my version of __match__, except for two things. </div></div></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="auto"><div><br></div><div>I didn't think through keyword arguments. You've partly solved the problem, but only partly. For example, Foo(a, b, key=c) and Foo(a, sub=b, key=c) may be identical calls, but the deconstructor can only return one of them, and it may not even be the one that was used for construction. I think something like inspect's argspec objects may handle this, but I'd have to work it through.</div></div></blockquote><div><br></div><div><div class="gmail_default" style="font-family:monospace">I think you could deal with that okay:</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">class type:<br></div></div><div class="gmail_default" style="font-family:monospace"> def __deconstruct__(cls, obj):</div><div class="gmail_default" style="font-family:monospace"> if not isinstance(obj, cls):</div><div class="gmail_default" style="font-family:monospace"> raise NotDeconstructable</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> result = obj.__dict__.copy()</div><div class="gmail_default" style="font-family:monospace"> if callable(cls.__init__):</div><div class="gmail_default" style="font-family:monospace"> argspec = inspect.getargspec(cls.__init__)</div><div class="gmail_default" style="font-family:monospace"> for i, argname in enumeraate(argspec[1:]):</div><div class="gmail_default" style="font-family:monospace"> result[i] = result.get(argname, None)</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> return result</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">So if your have def __init__(a, b, key) and you just store them as attributes, you can access them by name or by the same position as per __init__. I guess you could deconstruct *args and **kwargs too, by pulling out the unused values from result, though you'd probably need to use an ordereddict to keep track of things then. Though that's how recursive unpacking of lists work anyway, isn't it? Match against [head, *tail]? The only other difference is I was just ignoring unspecified bits, maybe it would make more sense to require you to unpack everything in the same way list/tuple unpacking does.</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> Foo(x, y, *args, kw=z, **kwargs) = foo</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">becomes something like:</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> __unpack = Foo.__deconstruct__(foo)</div><div class="gmail_default" style="font-family:monospace"> __seen = set()</div><div class="gmail_default" style="font-family:monospace"> __it = iter(__unpack)</div><div class="gmail_default" style="font-family:monospace"> <br></div><div class="gmail_default" style="font-family:monospace"> x = __unpack.pop( next(__it) )</div><div class="gmail_default" style="font-family:monospace"> y = __unpack.pop( next(__it) )</div><div class="gmail_default" style="font-family:monospace"> z = __unpack.pop( "kw" )</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> args = [ __unpack.pop(__i) for __i in __unpack ]</div><div class="gmail_default" style="font-family:monospace"> kwargs = { k: __unpack.pop(k) for k in __unpack.keys() }</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> if __unpack:</div><div class="gmail_default" style="font-family:monospace"> raise ValueError("too many values to unpack")</div><div><br></div><div><div class="gmail_default" style="font-family:monospace">You'd need something fancier than an ordereddict for *args to leave anything for **kwargs to pick up though.</div></div><div><br></div><div><div class="gmail_default" style="font-family:monospace">I think that adds up to letting you say:</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> re_point = re.compile('(?P<x>[0-9]+)-(?P<y>[0-9])+')<br></div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> case '1-3' is:</div><div class="gmail_default" style="font-family:monospace"> re_point(x=xcoord, y=ycoord):</div><div class="gmail_default" style="font-family:monospace"> print("X coordinate: %s, Y coordinate: %s" % (xcoord, ycoord))</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">by defining</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> class SRE_Pattern:</div><div class="gmail_default" style="font-family:monospace"> def __deconstruct__(self, obj):<br></div><div class="gmail_default" style="font-family:monospace"> m = self.match(obj)</div><div class="gmail_default" style="font-family:monospace"> if m is None:</div><div class="gmail_default" style="font-family:monospace"> raise NotDeconstructable</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> result = m.groupdict()</div><div class="gmail_default" style="font-family:monospace"> return result<br></div><div class="gmail_default" style="font-family:monospace"><br></div></div><div class="gmail_default" style="font-family:monospace">which seems plausible. That seems like a prtty friendly syntax for dealing with regexps actually...</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">(I think that's an advantage of having the protocol be Foo.__deconstruct__(obj) rather than obj.__match__() -- I don't think you could handle regexps without having both params)</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">Hmm, as far as literal-checking versus binding goes, if you're using := or "as" to bind subexpressions, as in:</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> case order is:</div><div class="gmail_default" style="font-family:monospace"> customer, Breakfast(spam, eggs, beans) as breakfast: ...</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">or</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> case order is:</div><div class="gmail_default" style="font-family:monospace"> customer, breakfast:=Breakfast(spam, eggs, beans)</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">then I /think/ you could use that to distinguish between binding and checking. So:</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> customer = Customer("alice")</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> name = "bob"</div><div class="gmail_default" style="font-family:monospace"> case customer is:</div><div class="gmail_default" style="font-family:monospace"> Customer(name):</div><div class="gmail_default" style="font-family:monospace"> # succeeds, binds name as "alice"</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"><div class="gmail_default"> case customer is:</div><div class="gmail_default"> Customer("bob"): </div><div class="gmail_default"> # compares "alice" to "bob", fails</div><div class="gmail_default"> # if it had succeeded, wouldn't have bound any variables</div><div><br></div><div> name = "bob"</div><div><div class="gmail_default"> case customer is:</div><div class="gmail_default"> Customer(name as x):</div><div class="gmail_default"> # compares "alice" to name ("bob") and fails</div><div class="gmail_default"> # but would have bound x as "alice" if it had succeeded</div></div><div><br></div><div>That seems like a rule that's reasonably understandable to me?</div><div><div class="gmail_default"><br></div><div class="gmail_default">("expr as name" has definitely grown on me over "name := expr")</div><div class="gmail_default"><br></div></div><div class="gmail_default"><br></div><div class="gmail_default"><br></div></div><div class="gmail_default" style="font-family:monospace">I guess I'm thinking "literal" matching is just a special case of deconstructing in general, and done by a method like:</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"><div class="gmail_default"> class object:</div><div class="gmail_default"> def __deconstruct__(self, obj):</div><div class="gmail_default"> if self == obj:</div><div class="gmail_default"> return ()</div><div class="gmail_default"> else:</div><div class="gmail_default"> raise NotDeconstructable</div></div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">That would have the potential side-effect of allowing things like:</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> 0 = n</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">as a valid statement (it'd raise NotDeconstructable if 0 != n). Not sure that's much worse than:</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace"> [] = n</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">though, which is already allowed. You could have a syntax error if there wasn't anything to bind though.</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">Cheers,<br></div><div class="gmail_default" style="font-family:monospace">aj</div><div class="gmail_default" style="font-family:monospace"><br></div></div>-- <br><div>Anthony Towns <<a href="mailto:aj@erisian.com.au" target="_blank">aj@erisian.com.au</a>></div>
</div></div>