
My original dict unpacking proposal was very short and lacked a motivating usage. Toy examples made my proposal look unnecessarily verbose and suggested obvious alternatives with easy current syntax. Nested/recursive unpacking is much more troublesome, especially when combined with name-binding. I wrote an example to compare my proposal with current syntax. Example usage. https://github.com/selik/destructure/blob/master/examples/fips.py Implementation. https://github.com/selik/destructure/blob/master/destructure.py The design of my module I'm least happy with is the name-binding. I extended a SimpleNamespace to create an Erlang-style distinction between bound and unbound names. Though the API is a bit awkward, now that the module is built, I'm less enthusiastic about introducing new syntax. Funny how that works. I haven't yet decided how to add post-binding guards to the cases.

On 28 May 2016 at 08:22, Michael Selik <michael.selik@gmail.com> wrote:
My original dict unpacking proposal was very short and lacked a motivating usage. Toy examples made my proposal look unnecessarily verbose and suggested obvious alternatives with easy current syntax.
Nested/recursive unpacking is much more troublesome, especially when combined with name-binding. I wrote an example to compare my proposal with current syntax.
Example usage. https://github.com/selik/destructure/blob/master/examples/fips.py
Implementation. https://github.com/selik/destructure/blob/master/destructure.py
The design of my module I'm least happy with is the name-binding. I extended a SimpleNamespace to create an Erlang-style distinction between bound and unbound names. Though the API is a bit awkward, now that the module is built, I'm less enthusiastic about introducing new syntax. Funny how that works.
I haven't yet decided how to add post-binding guards to the cases.
Interesting! Thanks for taking the time to make a real-world use case. I haven't looked at the module yet, just the example, but the code does look pretty clean and readable. The example is certainly complex enough that I'd probably end up with pretty messy and fragile code if I just tried to put something together with pure Python code. And yes, it's interesting how finding a good API for a module can make the need for a dedicated syntax less pressing. But working out that good API can be really hard (I don't think I'd ever have thought of doing it the way you did). Paul

I'm sure y'all are too busy with PyCon to be checking email. But if you're curious, I added attribute unpacking and post-binding guards ( https://github.com/selik/destructure). The object unpacking works as most folks have suggested, via kwargs. The guards unfortunately need to be functions that take no parameters. match(schema=Foo(a=1, b=bind.x), data=Foo(a=1, b=2), lambda : bind.x > 0) On Sat, May 28, 2016 at 6:26 AM Paul Moore <p.f.moore@gmail.com> wrote:
On 28 May 2016 at 08:22, Michael Selik <michael.selik@gmail.com> wrote:
My original dict unpacking proposal was very short and lacked a motivating usage. Toy examples made my proposal look unnecessarily verbose and suggested obvious alternatives with easy current syntax.
Nested/recursive unpacking is much more troublesome, especially when combined with name-binding. I wrote an example to compare my proposal with current syntax.
Example usage. https://github.com/selik/destructure/blob/master/examples/fips.py
Implementation. https://github.com/selik/destructure/blob/master/destructure.py
The design of my module I'm least happy with is the name-binding. I extended a SimpleNamespace to create an Erlang-style distinction between bound and unbound names. Though the API is a bit awkward, now that the module is built, I'm less enthusiastic about introducing new syntax. Funny how that works.
I haven't yet decided how to add post-binding guards to the cases.
Interesting! Thanks for taking the time to make a real-world use case. I haven't looked at the module yet, just the example, but the code does look pretty clean and readable. The example is certainly complex enough that I'd probably end up with pretty messy and fragile code if I just tried to put something together with pure Python code.
And yes, it's interesting how finding a good API for a module can make the need for a dedicated syntax less pressing. But working out that good API can be really hard (I don't think I'd ever have thought of doing it the way you did).
Paul

I see a little problem if we want to match more cases: from destructure import Binding, Switch o = Binding() schema1 = [2, o.x, 3] schema2 = [2, 4, o.x] s = Switch([2, 4, 3]) if s.case(schema1): print(o.x) if s.case(schema2): print(o.x) 4 --------------------------------------------------------------------------- BindError Traceback (most recent call last) [...] BindError: name 'x' has already been bound to 3 I prefer to enable multiple matches. But if you really like to forbid it then you would probably like to change next line -> raise BindError(fmt.format(name=name, value=value)) to line -> raise BindError(fmt.format(name=name, value=getattr(self, name))) Anyway I really like your proof of concept! :) To improve it maybe you could distinguish cases in readme.rst. For example: schema1 = [1, o.x, 3] schema2 = [2, 4, o.x] s = Switch([2, 4, 6]) if s.case(schema1): print(o.x) elif s.case(schema2): print(o.x) else: print('otherwise') 6 2016-05-30 8:52 GMT+02:00, Michael Selik <michael.selik@gmail.com>:
I'm sure y'all are too busy with PyCon to be checking email. But if you're curious, I added attribute unpacking and post-binding guards ( https://github.com/selik/destructure).
The object unpacking works as most folks have suggested, via kwargs. The guards unfortunately need to be functions that take no parameters.
match(schema=Foo(a=1, b=bind.x), data=Foo(a=1, b=2), lambda : bind.x > 0)
On Sat, May 28, 2016 at 6:26 AM Paul Moore <p.f.moore@gmail.com> wrote:
On 28 May 2016 at 08:22, Michael Selik <michael.selik@gmail.com> wrote:
My original dict unpacking proposal was very short and lacked a motivating usage. Toy examples made my proposal look unnecessarily verbose and suggested obvious alternatives with easy current syntax.
Nested/recursive unpacking is much more troublesome, especially when combined with name-binding. I wrote an example to compare my proposal with current syntax.
Example usage. https://github.com/selik/destructure/blob/master/examples/fips.py
Implementation. https://github.com/selik/destructure/blob/master/destructure.py
The design of my module I'm least happy with is the name-binding. I extended a SimpleNamespace to create an Erlang-style distinction between bound and unbound names. Though the API is a bit awkward, now that the module is built, I'm less enthusiastic about introducing new syntax. Funny how that works.
I haven't yet decided how to add post-binding guards to the cases.
Interesting! Thanks for taking the time to make a real-world use case. I haven't looked at the module yet, just the example, but the code does look pretty clean and readable. The example is certainly complex enough that I'd probably end up with pretty messy and fragile code if I just tried to put something together with pure Python code.
And yes, it's interesting how finding a good API for a module can make the need for a dedicated syntax less pressing. But working out that good API can be really hard (I don't think I'd ever have thought of doing it the way you did).
Paul

On Mon, May 30, 2016 at 4:48 PM Pavol Lisy <pavol.lisy@gmail.com> wrote:
I prefer to enable multiple matches.
Ok. Preventing rebinding was inspired by Erlang, but I can see why you might want to match multiple cases. I'll move that feature to a FreezeBinding class; I figure it'll be helpful in multithreading.
But if you really like to forbid it then you would probably like to change next line -> raise BindError(fmt.format(name=name, value=value)) to line -> raise BindError(fmt.format(name=name, value=getattr(self, name)))
Fixed.
Anyway I really like your proof of concept! :)
Thanks for reading.

On 28.05.2016 12:26, Paul Moore wrote:
On 28 May 2016 at 08:22, Michael Selik <michael.selik@gmail.com> wrote:
My original dict unpacking proposal was very short and lacked a motivating usage. Toy examples made my proposal look unnecessarily verbose and suggested obvious alternatives with easy current syntax.
Nested/recursive unpacking is much more troublesome, especially when combined with name-binding. I wrote an example to compare my proposal with current syntax.
Example usage. https://github.com/selik/destructure/blob/master/examples/fips.py
Implementation. https://github.com/selik/destructure/blob/master/destructure.py
The design of my module I'm least happy with is the name-binding. I extended a SimpleNamespace to create an Erlang-style distinction between bound and unbound names. Though the API is a bit awkward, now that the module is built, I'm less enthusiastic about introducing new syntax. Funny how that works.
I haven't yet decided how to add post-binding guards to the cases. Interesting! Thanks for taking the time to make a real-world use case. I haven't looked at the module yet, just the example, but the code does look pretty clean and readable. The example is certainly complex enough that I'd probably end up with pretty messy and fragile code if I just tried to put something together with pure Python code.
And yes, it's interesting how finding a good API for a module can make the need for a dedicated syntax less pressing. But working out that good API can be really hard (I don't think I'd ever have thought of doing it the way you did).
That's exactly what I had in mind. :) Far more flexible than a monolithic switch-case and looks quite Pythonic (= to me: minimum amount of special characters ;-) ). Thanks a lot, Michael. Best, Sven

On Wed, Jun 1, 2016 at 9:37 AM Sven R. Kunze <srkunze@mail.de> wrote:
more flexible than a monolithic switch-case
That wasn't my initial intention, but I guess that's a side-effect of needing to break it down into pieces viable in current syntax. Thanks a lot, Michael.
You're welcome. I hope the effort informs the discussion of a special matching syntax. Some things that really stood out as tough to implement or not possible. 1. Avoid NameErrors when specifying identifiers to bind in a schema. I see two options to implement this in current syntax. I chose to require a binding object and all identifiers must be attributes of that binding object. Another option would be postponing the lookup of the identifiers by requiring the schema to be defined in a function, then doing some magic. 2. Matching an object and unpacking some of its attributes. If the schema is a fully-specified object, we can rely on its __eq__ to do the match. If the schema specifies only the type and does not unpack attributes, that's even easier. The tough part is a schema that has a half-specified object where where its __eq__ cannot be relied on. If all Unbounds are equal to everything, that would help a half-specified schema compare well, but it breaks all sorts of other code, such as ``list.index``. I chose to compare all public, non-Unbound attributes, but that may not have been the implementation of its __eq__. Alternatively, one could trace __eq__ execution and revise Unbound comparisons, but that's beyond my wizarding abilities. 3. Unpacking the attributes of an object that aggressively type-checks input. Given my solution of a Binding object and unpacking into its attributes, one cannot, for example, unpack the first argument of a ``range`` as it raises a TypeError: ``bind=Binding(); schema = range(bind.x)``. I'm considering removing object-unpacking from the module. To match and unpack an object, the schema could be specified as a mapping or sequence and the user would transform the object into a mapping (like ``obj.__dict__``) before doing the match.

On 01.06.2016 21:04, Michael Selik wrote:
On Wed, Jun 1, 2016 at 9:37 AM Sven R. Kunze <srkunze@mail.de <mailto:srkunze@mail.de>> wrote:
more flexible than a monolithic switch-case
That wasn't my initial intention, but I guess that's a side-effect of needing to break it down into pieces viable in current syntax.
Which I for one doesn't regard as a drawback.
Thanks a lot, Michael.
You're welcome. I hope the effort informs the discussion of a special matching syntax.
Some things that really stood out as tough to implement or not possible.
1. Avoid NameErrors when specifying identifiers to bind in a schema.
I see two options to implement this in current syntax. I chose to require a binding object and all identifiers must be attributes of that binding object. Another option would be postponing the lookup of the identifiers by requiring the schema to be defined in a function, then doing some magic.
2. Matching an object and unpacking some of its attributes.
If the schema is a fully-specified object, we can rely on its __eq__ to do the match. If the schema specifies only the type and does not unpack attributes, that's even easier. The tough part is a schema that has a half-specified object where where its __eq__ cannot be relied on. If all Unbounds are equal to everything, that would help a half-specified schema compare well, but it breaks all sorts of other code, such as ``list.index``. I chose to compare all public, non-Unbound attributes, but that may not have been the implementation of its __eq__. Alternatively, one could trace __eq__ execution and revise Unbound comparisons, but that's beyond my wizarding abilities.
3. Unpacking the attributes of an object that aggressively type-checks input.
Given my solution of a Binding object and unpacking into its attributes, one cannot, for example, unpack the first argument of a ``range`` as it raises a TypeError: ``bind=Binding(); schema = range(bind.x)``.
I'm considering removing object-unpacking from the module. To match and unpack an object, the schema could be specified as a mapping or sequence and the user would transform the object into a mapping (like ``obj.__dict__``) before doing the match.
This all matches with my expectations I expressed on the other thread. Maybe, it's another indication that one should not split attributes from an object at all but rather use the object as a whole. This said, lists/tuples and dictionaries are more like a loose collection of separate objects (allowing unpacking/matching) whereas an object represents more an atomic entity. Sven
participants (4)
-
Michael Selik
-
Paul Moore
-
Pavol Lisy
-
Sven R. Kunze