The fact that there is more than you want is precisely why you want to make classes for the data you *do* want.
I do have kit for streaming JSON out directly without creating JSON data internally, but I'm still trying to find a good way to stream JSON in creating wanted objects on-the-fly so that the unwanted data are never stored. I think it can be done from the same schema language that I use to generate classes. On Mon, 28 Feb 2022 at 20:09, Kasper Osterbye <kasper.oster...@gmail.com> wrote: > 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> > 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 > > >