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

Reply via email to