5 ms·
This is an excellent article! In functional languages this is called "destructuring assignment". This article could be a great jumping off point into namedtup
by sitkack 5y ago
This is an excellent article!
In functional languages this is called "destructuring assignment".
This article could be a great jumping off point into namedtuple[1], the walrus operator [2] and the Pattern Matching[3] (Python 3.10), which assist into getting back into the symbolic domain from the structural domain, though you will have to come up with names! :)
Remember that namedtuple has the following additional methods
_make Class method that makes a new instance from an existing sequence or iterable
_asdict Return a new dict which maps field names to their corresponding values
_replace Return a new instance of the named tuple replacing specified fields with new values
_fields Tuple of strings listing the field names
_field_defaults Dictionary mapping field names to default values.
[1] https://docs.python.org/3/library/collections.html#collections.namedtuple https://docs.python.org/3/library/collections.html#collectio...
[2] https://peps.python.org/pep-0572/ https://peps.python.org/pep-0572/
[3] https://peps.python.org/pep-0636/ https://peps.python.org/pep-0636/
- agumonkey 5y agoone step at a time people will realize mainstream imperative programming and its derivative has been a long waste of time
- nerdponx 5y agoI don't think that's a valid conclusion here at all.
- agumonkey 5y agothe amount of code that was - point to a structure / object - declare a variable - access the object / structure field and assign it to var - repeat for every information you want to extract from the struct / object is large and now replaced by something transparent
- Espressosaurus 5y agoIn what way is multiple assignment and tuple unpacking not imperative code?
- sitkack 5y agoAssignment is not necessarily mutation. Every language, including functional ones can assign values to names. You have unlocked +1 reductionist pedantry.
- agumonkey 5y agoYou might never create any actual variable, you point to access/ reference reads passed to other functions. Of course in python it's only syntactic sugar but bit by bit culture will evolve.
- nerdponx 5y agoMemory-managed and dynamically typed programming languages already solve this boilerplate problem mostly. If anything, the primary advantages of functional programming languages are not functional purity, referential transparency, or elegant abstractions; but HM-style type systems, typeclasses, and tidy succinct syntax.
- agumonkey 5y agoI believe HM was derived from structural and purely functional experience.
- bmitc 5y ago> In functional languages this is called "destructuring assignment". I thought it's just called pattern matching. Most functional languages don't have assignment and have bindings instead.
- nerdponx 5y agoI would argue that destructuring is a special case of pattern matching.
- raymondh 5y agoIOW, the match/case tool can pattern match and destructure at the same time. Som uninterestingly, it is possible to do only one of those things: Iterator unpacking: first, *middle, last = t is similar to a match/case with a sequence pattern: match t: case first, *middle, last: ... The former works for any iterable and the latter requires a sequence. The former raises an error for a non-iterable and the latter just skips the case for a non-sequence. Otherwise, they are similar. However, arguing that one is a special case of the other misses the entire point of structural pattern matching. We would almost never substitute the latter for the former. They mostly live in different worlds because we use them and think about them much differently: # Something that would work but they we don't do for item in somedict.items(): match item: case [key, value]: print(key.upper()) print(value) print() case _: raise RuntimeError('unexpected kind of item') # What we actually do for key, value in somedict.items(): print(key.upper()) print(value) print() Sorry for the small rant, but I think this pedantic point isn't a useful line of thinking. That concepts and implementation have some overlap but should occupy a different concept maps in our brains — very much like the idea that dicts can do set-like things but that we're better off thinking about dicts as doing lookups and sets as doing uniquification, membership-testing, and set-to-set operations.
- greggyb 5y ago"Pattern matching", in the context of functional programming languages, most often refers to the complex combination of destructuring bind and conditional control flow based on which binding succeeded. This article describing the Python feature of multiple assignment demonstrates only the destructuring assignment (assignment, because it is Python and these variables can be reassigned). There is no control flow tied to this operation, except for the exceptions that can be thrown when the LHS and RHS have incompatible structure. Clojure is another example of a language with destructuring binding that is not tied to a control flow construct. Clojure functions and `let` bindings can use a destructuring syntax to bind to elements in a data structure. There is no tightly-linked control flow structure that goes along with this binding mechanism. It is my understanding that there are several libraries available that implement pattern matching, but I do not use these. Most often, when I have seen pattern matching described to an audience with a background in imperative languages, it is compared to a super-powered switch statement. This gives the impression that the important part is the control flow. Languages such as Python and Clojure show that there is inherent value in the destructuring assignment/bind, even out of the context of control flow.
- Someone 5y agoWhy that underscore? I thought that’s a signal for “method is for internal use, only”. Also (but a bit minor, given that Google tells me Python’s named tuples are immutable, so I guess programmers will know that): if I were to read foo._replace(bar=42) I would think that to modify foo. Because of that, I like scala’s foo.copy(bar=42) more. It makes it clearer that you have to do foo = foo.copy(bar=42) to ‘change’ the value of foo (actually: bind it to a new value).
- rjmorris 5y agoThe underscore is to avoid conflicts with your own attributes named make, asdict, etc. So you could do: MyClass = namedtuple("MyClass", ["make", "asdict", "fields"]) x = MyClass(make=10, asdict=11, fields=12) print(x.asdict) The "underscore-to-avoid-naming-conflicts" approach is used elsewhere in the Python standard library, too, although another example isn't coming to me at the moment.
- nerdponx 5y agoPerhaps even weirder, the Enum class uses names like `_value_`. At one time, inventing nonstandard "magic method" names like `__value__` was considered off-limits, because in principle those names could later be used by the Python language spec itself. In practice, such additions have been very rare, and library authors have become a lot less squeamish about defining their own. The core data model of "almost everything is a mutable lookup table" can be easy to work with and reason about at times, but sometimes it's a little too simple, and you end up with these kinds of ad-hoc and inconsistent workarounds even in the standard library.
- BerislavLopac 5y agoThat is correct, although I agree with the GP that it's a bit unclear. My preferred practice for this is to put the underscore at the end, so it would be make_ and asdict_