Thanks Richard, I tend to agree with your design experience. As it happens, in my concrete code I get JSON back from a github api which serves me more than I want. I am therefore not interested in making classes for those aspects of the data I throw out.
The way I handle this in practice myself is to introduce a local variable for each level. Makes it easier to debug too. Best, Kasper > On 27 Feb 2022, at 00.42, Richard O'Keefe <rao...@gmail.com> wrote: > > I was bemused to realise that I've actually been programming > for literally 50 years. It finally seeped through my thick > skull I make enough mistakes that if it's hard to read, I > probably got it wrong. And THIS code fragment is hard to > read. I don't care if you use parentheses or pipes, it's > hard to read either way. Oh, the pipes make the structure > easier to see, true. But I am still left guessing what it > is FOR. What's it SUPPOSED to mean? And the problem is that > there are some abstractions missing. Of course this is JSON's > fault. > > This is the first code smell, and in my experience it is > almost ALWAYS a code smell: you are using JSON as a > "processing" data structure instead of as a "communication" > syntax. Using JSON inside your program, instead of at the > edges, means that the semantics gets scattered all over the > code, which leads to complexity and inconsistency. > > The convention I follow is that data structure more complicated > than a string or an array of numbers has a Smalltalk class > Whatsit>>asJson converts an instance to a JSON value > Whatsit class>>fromJson: converts a JSON value to an instance. > I'll start with > topLevel := TopLevelWhatsit fromJson: someStream nextJson. > and from then on I'm working with pure Smalltalk. > > This has the extra benefit of validating the input right away. > I don't want my program running for several hours and then > running into trouble because some component of the JSON value > is missing or ill-formed. > > In practice, the time cost of converting JSON to (application- > specific) Smalltalk is quite small compared with the cost of > reading the JSON in the first place, and there can be seriously > worthwhile savings in memory. > > For this example, we'd be looking at > topLevel tree blobs > collect: [:each | each path] > thenSelect: [:path | path hasAnyExtension: #('md' 'mic')] > > My Filename class has methods > has[Any]Extension:[ignoringCase:] > which I have shamelessly exploited here. I like the > way that they hide the structure of a Filename. > > The portmanteau methods > Collection>>collect:thenSelect: > Collection>>select:thenCollect: > have existed in Pharo as long as Pharo has existed. > They have two rationales: > (a) improve readability > (b) permit optimisation by eliminating an intermediate collection. > Sequences exploit (b). Sets could too but happen not to. > > THEREFORE, here are free implementations of > Set>>collect:thenSelect: and Set>>select:thenCollect: > consistent with Pharo's Set>>collect: > > collect: collectBlock thenSelect: selectBlock > "Override Collection>>collect:thenSelect: like OrderedCollection. > Beware: 'self species' is often inappropriate, as in #collect:." > |newSet| > newSet := self species new: self size. > self do: [:each | > |item| > item := collectBlock value: each. > (selectBlock value: each) ifTrue: [newSet add: item]]. > ^newSet > > select: selectBlock thenCollect: collectBlock > "Override Collection>>select:thenCollect: like OrderedCollection. > Beware: 'self species' is often inappropriate, as in #collect:." > |newSet| > newSet := self species new: self size. > self do: [:each | > (selectBlock value: each) ifTrue: [ > newSet add: (collectBlock value: each)]]. > ^newSet > > > > On Wed, 26 Jan 2022 at 22:19, Kasper Osterbye <kasper.oster...@gmail.com > <mailto:kasper.oster...@gmail.com>> wrote: > Cheers all > > I have noticed that I often ends up with quite a number of nested > expressions, for example: > > (((json at: 'tree') > select: [ :e | (e at: 'type') = ‘blob' ]) > collect: [:e | Path from: (e at: 'path')]) > select: [ :p | p segments last > in: [ :name | (name endsWith: '.md') | (name > endsWith: '.mic') ] ] > > What kind of proposals (if any) have been for a different syntax which could > give a more streamlined syntax? > > My own thinking has been around an alternative to the cascade semicolon. What > symbol to use does not matter for me, but something like > json at: ‘tree' º > select: [ :e | ((e at: 'type') = 'blob’)]º > collect: [:e | Path from: (e at: 'path’)]º > select: [ :p | p segments last > in: [ :name | (name endsWith: '.md') | (name endsWith: > '.mic') ] ] > > Basically, a send the right hand expression to the result of the left hand > expression. > > Has anyone ever tried this, or is it just one of the many small annoyances > best left alone? > > Best, > > Kasper