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

Reply via email to