<<< Introduction | <<< Setting Up | Words, words, words >>>
Telling Stories
(for which the anagram finder gave me I snort gilt eels. It's a lie, I tell you!)
In this article I'll describe how to code the anagram finder web app. As before, my target audience is people with levels of technical skill ranging from near-beginner to intermediate.
In a previous article (not in this series) I wrote about Story Driven Design. Stories are written by customers who want a website built. These people are usually not coding experts but they know what they want and how to describe it. Here we have a pretty simple web app so the stories should be simple too. Here's the app:
and here are the stories that describe how it looks and functions:
"The app presents a text field in which to type a line of text, a button labeled Run and another labeled Clear."
"When the app starts, the text field is preloaded with the text it held the last time it was used (if this is not the first time)."
"When the user clicks Run the program calls the anagram finder to look for dictionary words that together use up all the letters in the text given. The label of the button changes to Stop."
"When Stop is clicked, the search stops and the button label changes to Continue."
"When Continue is clicked, the search resumes."
"When Clear is clicked the list of results is cleared."
"The page displays the list of anagrams as they are found, in a panel below the text and buttons, sorted alphabetically."
and so on.
How to code stories
Stories of this kind form the basis of a contract between the customer and the programmer and are what the delivered product will be verified against. The stories themselves are rarely cast in stone; they frequently undergo changes in the light of experience and feedback from users, but by contrast the anagram finder algorithm is likely to be fixed for all time. So it makes little sense to combine both these items into the same coding structure. Instead, we have two distinct programming paradigms:
At the heart of the web app is the anagram finder; a component that takes a string and returns a list of strings. This works best as a standalone JavaScript module that exposes whatever API is required to do that job. You may have written it yourself or you may have found a suitable component in a repository somewhere. It's quite likely that neither you nor your customer will ever have the need to poke about inside it.
Around this is the user interface, implementing the user stories, which are likely to be subject to regular change and refinement. You can do this in regular JavaScript but the likelihood is that only you, the original author, will be able to read and understand it at a glance. Anyone else will have to pick their way through it each time. I argue regularly that this represents a potential maintenance liability and an addition to the Total Cost of Ownership (TCO) of the project, so it's here that we should be willing to consider alternative techniques.
Why EasyCoder?
There are few web pages so complex they can only be described using advanced computer code. Every one I can think of can be - and usually is - described in English, and it's very helpful if the code keeps its stories visible so the owners of products can look inside them and verify they do as expected.
I have met dedicated JavaScript/React programmers who disagree strongly with this. They believe that their customers should be discouraged from looking into their code, but I think they are missing a key point. If your customers - the domain experts - are able to read your code then they may spot something you overlooked. Customers are not fools and many take a keen interest in seeing what they have paid for. They have insights that we as programmers may lack.
EasyCoder is an English-like programming language, designed for browsers and delivered as a WordPress plugin, that looks very unlike most other programming languages. It's designed to let you code with scripts that visibly correspond to the original stories; scripts that can be read by the domain expert who wrote the stories as well as by the programmer who turned them into code. When changes are needed it's much easier to do it at this level than where the stories are buried in a huge mass of JavaScript that only a skilled programmer can read.
At the very least you can regard the following as pseudo-code that expresses user stories in a verifiable form, even if you then choose to re-implement it with your favorite tools.
The EasyCoder script
EasyCoder scripts live in the web page or are loaded from somewhere else. They are simply chunks of text that must be compiled by the EasyCoder plugin before they can do anything.
Because we're building the app as a WordPress page we don't have access to the root of the DOM tree (the <body>
element), so to provide somewhere for the app to live we need to add a <div>
to the page, as follows, with an id the script will look for and attach one of its own variables:
<div id="anagrams"></div>
The EasyCoder script lives in a special <pre>
block:
<pre id="easycoder-script">
...
</pre>
When the page has loaded, the EasyCoder plugin looks for the <pre>
block, compiles its contents and runs them.
The stories tell us we need an input box for the user to type some text. We'll also have some buttons and a panel to hold the anagrams as they arrive. Here's the complete EasyCoder script that corresponds to the stories:
h2 Title
div Root
div InputDiv
div ResultsDiv
div ResultDiv
div Label
div Padding
input Text
button RunButton
button ClearButton
variable Anagrams
variable Phrase
variable Words
variable Index
variable Running
variable Results
variable Keys
! Attach to the DOM element on the page
attach Root to `anagrams`
! Styling is different for mobile
if mobile set the style of Root to `width:100%`
else set the style of Root to `width:100%;margin:1em`
create Title in Root
set the style of Title to `text-align:center`
set the content of Title to `Anagram Finder`
! Create a separate DIV for all the form components
create InputDiv in Root
if mobile set the style of InputDiv to `display:flex;margin: 0.5em`
else set the style of InputDiv to `display:flex;margin-top:1em`
! "Padding" is reused every time we want a separator
create Padding in InputDiv
set the style of Padding to `flex:2`
create Text in InputDiv
set the style of Text to `flex:76`
! Retrieve the text from last time
get Phrase from storage as `anagram-text`
set the text of Text to Phrase
create Padding in InputDiv
set the style of Padding to `flex:2`
create RunButton in InputDiv
set the style of RunButton to `flex:10`
set the text of RunButton to `Run`
create Padding in InputDiv
set the style of Padding to `flex:2`
create ClearButton in InputDiv
set the style of ClearButton to `flex:10`
set the text of ClearButton to `Clear`
on click ClearButton
begin
clear ResultsDiv
clear Results
end
! A label to go under the form
create Label in Root
set the style of Label to `margin: 0.5em 0.5em 0 0.5em`
set the content of Label to `Loading a word list...`
! The DIV to hold all the found anagrams
create ResultsDiv in Root
set the style of ResultsDiv to `margin: 0.5em 0.5em 0 0.5em`
! Load the anagram finder JS file
require `https://cors.io/?https://raw.githubusercontent.com/gtanyware/EasyCoder/master/demo/anagrams.js`
! Load a list of 33,300 English words
require `https://cors.io/?https://raw.githubusercontent.com/gtanyware/EasyCoder/master/demo/words.js`
set the content of Label to `No anagrams found (yet):`
on click RunButton go to Run
! Wait for the user to do something
stop
! When the user clicks Run...
Run:
put Text into storage as `anagram-text` ! Remember it for next time
json set Results to object ! Make Results an empty JSON variable
Continue:
! Reprogram the button
set the text of RunButton to `Stop`
on click RunButton
begin
clear Running
! Reprogram it again
set the text of RunButton to `Continue`
on click RunButton go to Continue
end
! Set the running flag and keep going as long as it remains set
set Running
while Running
begin
! This is where we call the anagram finder library module
put anagrams of Text into Anagrams
! It returns JSON properties "status" and "words"
if property `status` of Anagrams is `found`
begin
put property `words` of Anagrams into Words
json sort Words
! Build a phrase with the words of this anagram
put empty into Phrase
put 0 into Index
while Index is less than the json count of Words
begin
put Phrase cat element Index of Words cat ` ` into Phrase
add 1 to Index
end
! Check if this phrase is already a property of Results
if property Phrase of Results is empty
begin
! It isn't, so add it, with an arbitrary value (that we don't use)
set property Phrase of Results to true
! Extract the phrases as an array
put the json keys of Results into Keys
json sort Keys
! Show how many results we have
set the content of Label to the json count of Keys cat ` anagrams found:`
! Rebuild the list of phrases
clear ResultsDiv
put 0 into Index
while Index is less than the json count of Keys
begin
create ResultDiv in ResultsDiv
set the content of ResultDiv to element Index of Keys
add 1 to Index
end
end
end
! Allow the CPU to cool off (critical!)
wait 2 ticks
end
stop
Anyone who was around in the '80s and came across HyperCard on the early Macintosh may find this slightly familiar-looking. This is not a coincidence; EasyCoder was inspired by HyperTalk, the language inside that "insanely great" product that lives on in part as AppleScript. If there's a guiding principle it's that readability is best ensured by minimal use of symbols and maximum adherence to plain English syntax.
EasyCoder looks very different to modern computer languages, having few symbols and lacking concepts like block structuring and parameterization that are familiar to experts but which confuse everyone who isn't a programmer. Like SQL it sits in the middle ground between the machine and the human and is understandable by both.
Now for how it works. I've annotated it pretty heavily with comments (the exclamation marks); more than is generally regarded as necessary for good documentation, but this is the first time you'll have seen this syntax. The first half of the script sets up the screen, starting with a list of variables. The convention is for names to all start with capital letters, like they do in English. Many of these are types that correspond to DOM elements; the rest are plain variables to hold numbers or text.
The first action is to attach the Root
variable to the <div>
we set up earlier. From now on, anything we do with or to Root
will act on the <div>
itself. We then test if the app is running in a smartphone browser, and apply suitable styling. As you can see, styles can be applied inline. Purists who object to this way of working are free to set a class attribute on each element and create a separate stylesheet, but it's far simpler for scripted elements to have styles set as they are created.
There then follows a series of commands that create the various parts of the display, item by item. In EasyCoder, when we create DOM elements we have to specify a parent element that already exists. I've put the text field and the buttons into their own <div>
and called it InputDiv
, then all the rest go straight into Root
. I'm hoping that even readers who are not programmers will find it easy to follow - that's the way it's intended to be.
The InputDiv
uses a flex
display attribute and I find it helps when distributing components to use percentages that add up to 100. There's a line that takes a value from storage and puts it into the text field, and another that saves the current field value when the Run button is clicked. These use browser storage, that lets you persist data between visits to a site.
Having set up the screen elements we now have a couple of require
lines. These commands load JavaScript files and insert them into the HEAD
of the document, and the strange URLs are needed to avoid cross-domain request refusals that exist to prevent browsers from accepting potentially dangerous executable code. One of these files is the anagram finder component we will be building in the next part of this series; the other is a dictionary - a list of words. It looks like this:
const EasyCoder_words = [
`a`,
`aah`,
`aardvark`,
`abacus`,
`abacuses`,
`abalone`,
...
and continues for another 33,000 or so lines. If you substitute a list of French, German or Spanish words it will work just as well but in the chosen language (though I have no idea how to deal with accented characters). Word lists can be found on the Internet with a bit of hunting.
These two items can take a while to download, so the text of Label
is used to inform the user that something is happening.
At the end of the screen setup code there's an on click
command to detect when the user clicks the Run
button and transfer control to the program label Run:
.
When it finishes setting up the program stops and wait for something to happen. When the click occurs, first there's the rather odd-looking command json set Results to object
. This initializes the variable to be an empty JavaScript object (i.e. not an array).
The script reprograms the Run
button so we can use it to stop the run, then enters a while
loop, where there's the line
put anagrams of Text into Anagrams
which is the syntax we'd like to use to call our anagram finder library, but here we have a problem. This syntax is unknown to EasyCoder so we're going to have to do something special to handle it. Before I say any more about that I'll just finish describing the code.
When we ask the anagram finder to look for anagrams, using the command above, it comes back with a list of words and a success/fail flag. We're only interested when the search succeeds, so when it fails (the more common occurrence) the whole of the rest of the code is skipped and we ask it to try again. It's most important that we put a short delay in, though, otherwise the computer, with no chance to rest, will rapidly overheat and fail to respond to anything except the power button. EasyCoder monitors its own code to prevent this kind of thing happening, but here all the work is being done by an external module. A delay of 20ms is long enough for it to cool a little, and just as importantly, because of JavaScript's single threading model this is the only opportunity for button clicks to be acted upon.
On the less frequent occasions where the search succeeds we take the list of words, sort them into alphabetical order and put them all on one line with a space between each one. Then we check the Results
object to see if the phrase we have was already there. If not, we add it as a property of that object, extract the object keys, sort them and write them to the results panel in the display, with a line at the top saying how many anagrams were found. Each anagram goes in its own <div>
on the off-chance we might want in the future to be able to click each one and do something with it.
You'll see the word json
occurring quite frequently. This is because the handling of JSON-formatted strings isn't standard EasyCoder functionality and is done by a plugin language package. To avoid the syntax becoming ambiguous, the plugin prefixes lesser-used JSON features with the word json
.
Extending the language
I promised I'd deal with the command to call the anagram finder module, which is unrecognized by standard EasyCoder. Other languages do this with functions but EasyCoder demands that all additions extend the language seamlessly. The language is comprised of plugin packages, each one handling a particular vocabulary and syntax, such as for Google Maps, SVG graphics or JSON, so this is a case for adding a new package, to implement a single command that calls the anagram finder. (Or you could add the command to an existing package, if you have one.)
It's not difficult to add packages but it's completely specific to EasyCoder. Since these articles are mostly about web apps I'll leave it to the end and put it in an appendix article. It's only going to be of interest to people who actually use EasyCoder. If anyone has been following the series but coding the UI directly in JavaScript they will have no trouble interfacing to the anagram finder that's coming up in the next part of the series.
Coming up...
In the next part of this series I'll show you what the anagram finder JavaScript module looks like. If in the meantime you'd like to play with anagrams you can find the app at https://easycoder.software/anagrams.
Title photo by Émile Perron on Unsplash
Top comments (2)
Thank you for this show case! Having put the code and then the explanation was a very good way to show that even without any understanding of the EasyCoder syntax it remains actually understandable. Great job! Cannot wait for the next part of this serie :)
Thank you K. The hardest part about writing is to figure who your audience is so you can get the level right for them without boring others with more experience. There's often no way to tell how well you do, so feedback is soooo useful.