Thursday, June 21, 2018

Javascript tools for Python hobbyists

I am just a hobbyist, Python enthusiast who has been, over the course of many years, writing what is now a relatively big Javascript program (close to 20,000 lines of code so far). If you like Python and the Pythonic way of programming, and find yourself writing more JavaScript code than you'd like for a fun side-project meant as a hobby, you may find some merit in the approach I use.  I wish I could have read something like this blog post when I started my project or even just a few years ago, when I did a major rewrite, and started using some of the tools described in this post.

If you are a professional programmer, you can just stop reading as you know much more than I do, and you surely have a better, more efficient and cutting edge way of doing things right now - and you will likely use yet a different way next year, if not next month. So, you would likely find my advice to look the same as my site: dated and not using the latest and coolest techniques - in short, for you, not worth looking at. ;-)

Summary:

  • If you must, choose a well-supported Javascript library and stick with it.
  • Use npm for installing Javascript tools
    • Avoid depending on third-party packages whenever possible
  • Use npm to manage your workflow
    • Supplement with your own Python or shell/batch script when needed
  • Use browserify to concatenate all your Javascript files
  • Use tape for unit testing
    • Use faucet for formatting of unit test results
  • Use QUnit for integration testing
  • Optional: use Madge for identifying circular dependencies
  • Optional: use Dependo to identify any overlooked module
  • Optional: use JSDoc for creating an API
  • Use jshint instead of jslint

Warning

This blog post is long. I've attempted to provide enough details for you to determine in each case if my use-case corresponds to yours and thus if and when my recommendation might make sense for you and your project.

The context

I started working on Reeborg's World many years ago. The first version was created as a desktop program (rur-ple) in 2004. My first primitive attempt at a web version was done around 2007.  During the years I have worked on it, tools and libraries have come, evolved, and gone, to be replaced by better ones.  As programming is only a hobby for me which I work on when I have some free time, I cannot afford to change the set of tools I use every year to follow the latest trend.

I've started working on the current version when using color gradients for buttons and menu bars was the latest and coolest thing - well before the current flat UI became the norm.


Admittedly, my site looks dated - but since I do not have enough time to add all the new ideas for functional improvements I want to make, investing time to modernize the look is not a priority.

The Javascript code I wrote is split over many files and has become a tangled mess - in spite of some occasional attempts at reorganizing the code, including a near-complete rewrite a few years ago.


Some of the complexity is required as I want to make it easier for would-be collaborator to add new programming language or paradigms for learners [1] or additional human language support [2]. However, it is likely that some of this tangled mess could be simplified with a significant effort.

In addition, there is more to Reeborg's World than a single site; there is also a basic programming tutorial available in three languages [3] with additional languages in the works, a Teacher's Guide [4], an API documentation for advanced features [5], and more [6].  Each of these act almost like an independent project pulling me in different directions.

In order to preserve my sanity, as my project slowly evolves I need some constancy and simplicity in the tools I use.

Using a well-supported library

Unlike the situation with Python, which comes "batteries included", there is no standard library for Javascript. Using a library means choosing between various alternatives, and communities.

When I started this project, the main problem facing people writing Javascript code was browser incompatibilities.  There was one obvious solution: use jQuery.  Nowadays, it is most likely no longer needed for that purpose, but that was not the case back then.

I also knew that I wanted the ability to have floating windows for additional menus and dialogs. After examining a few choices, I settled on jQuery UI, since there was good documentation for it and an active community ... and I was already using jQuery which meant a smaller footprint than some other alternatives.

Libraries like jQuery and jQuery UI can be included with a link to a CDN (Content delivery network) which can reduce the load on the server where my project lives. I can also link to a specific version of these libraries, which means that I do not have to update code that depend on them (except if security issues are discovered).

10 years later, both libraries are still alive and well and I haven't needed to make any significant changes to any code that uses them.

Use npm for installing Javascript tools

npm is described as both a package manager for Javascript and as the world's largest software respository. I use it to install various Javascript tools I use (like tape, browserify, jsdoc, etc. which I describe below). 

I do not use it to install javascript libraries (big or small) called by my own code. From what I can tell, the "best/most common" practice in the Javascript world is to make use of tons of modules found on the npm repository, some of which are simply one line of code.  Requiring a single module can mean in reality that the project can depend on dozens of other modules, none of them being vetted - unlike the Python standard library. Upgrade to a single modules can result in a bug affecting hundreds of other modules ...  For example, one developer broke Node, Babel and thousands of projects in 11 lines of JavaScript.

When I resume working on my project after months of inactivity, I never have to worry about how any change to any such third party module could require updating my code.  (Yes, there are most likely ways to mitigate such problems, but I prefer to avoid them in the first place.)

There are alternatives to npm (such as yarn, and others), but, from what I can tell, they do not offer any advantages when it comes to installing Javascript tools - a task that is performed very rarely for a given project.

Use npm to manage your workflow

When reading about Javascript, I most often saw either gulp or grunt mentioned mentioned as tools to automate tasks. From what I read, it seems that were essential to do any serious Javascript development.  Each of them came had its own way to do things ... and it was not easy for me to see which would be the best fit.  In the various posts I read about gulp vs grunt, npm was never mentioned as an alternative.

However, as I learned more about npm, I found that, together with a very simple batch file it could do all the automation that I needed in a very, very simple way, by defining "scripts" in a file named package.json.  Chaining tasks with npm scripts is a simple matter of "piping" them (with the | character).  Since I had already installed npm, it became an easy choice.

Use browserify to concatenate all your Javascript files

Once my Javascript code became much too long to fit into a single file, I broke it up into various files. With Python, I would have use an import statement in individual files to take care of dependencies. With Javascript, the only method that I knew of at the beginning of my project (10 years ago) was to add individual links in my html file. As the number of Javascript files increased, it became difficult to ensure that files were inserted in the proper order to ensure that dependencies were taken care of ... In fact, it soon became almost impossible. 

This required a major rewrite.  Fortunately, when I had to do this, some standardized way of ensuring dependencies had emerged.  The simplest was to use something like

 require("module_a.js");

at the top of, say module_b.js, and use some tools to concatenate the javascript files, ensuring that proper dependencies were taken care of.  The simplest tool I found for this purpose is browserify, originally created, as far as I can tell, by James Halliday.

browserify can be installed using npm.

Use tape for unit testing

Sigh ... I find testing boring ... But, as my project grew larger, it became necessary to write some tests.

When I did a search on testing tools/framework for Javascript, I most often saw mentions of Chai, Jasmine, Mocha QUnit and Sinon.  A recent search yields a few more potential candidates like Cucumber, Karma, etc.    

The Javascript world seems to really, really like so-called Behaviour Driven Development, where writing tests can mean writing code like:

tea.should.have.property('flavors').with.lengthOf(3);

If.I.wanted.to.write.code.that.read.like.English.I.would.likely.use.Cobol.

It is only by accident that I came accross tape as a testing framework that felt "right" to me. I like my tests to look like my code. With Python, I would use assert statements to ensure that the a function produces the correct result.  My favourite unit testing framework for Python is, not surprisingly, pytest.

From what I have seen, tape is the closest Javascript testing framework to pytest.  Here's an actual example where I test some code which is expected to raise/throw an exception/error:

test('add_wall: invalid orientation', function (assert) {
    assert.plan(2);
    try {
        RUR.add_wall("n", 1, 2, true);
    } catch (e) {
        assert.ok(e.reeborg_shouts, "reeborg_shouts");
        assert.equal(e.name, "ReeborgError", "error name ok");
    }
    assert.end();
});

I make use of "assert.plan()" to ensure that the number of assertions tested matches my expectations.

It was only after I had used tape for a while that I found out that it was also written by James Halliday.

tape can be installed using npm.

Use faucet for formatting of unit test results

Tape's output is in the TAP format (Test Anything Protocol) which, by default, is extremely verbose. Most often, it is recommended to pipe the results into formatters which produce more readable results. 

Depending on what I am doing, I use different formatters, some more verbose than others. After trying out about a dozen formatters, I now use faucet by default.  faucet can be installed using npm and has been written by, ... you guessed it, James Halliday.

Use QUnit for integration testing

Unit tests are fine, but they miss problems arising from putting all the code together.  I used different strategies to do integration testing, all of which seem to create almost more problems than they solved, until I stumbled upon a very easy way that just works for me.  Using a Python script, I take the single html file for my site, put all the code inside an html div with display set to none, insert some qunit code and my own tests, and let everything run.

Optional: use Madge for identifying circular dependencies

To help identify potential problems with circular dependencies, I use madge, which can be installed with npm.

There is one remaining dependency in my code, which I silence by not inserting a require() call in one of my modules: when the site is initialized, I want to draw a default version of the world which I by calling functions in the drawing module when loading some images. Later, when calling the drawing module, I do need the definitions found in the module where I load the images.  I could get rid of the dependencies at the cost of duplicating some code ... but since the initializing of the site and the execution of user-entered code are done in separate phases, the circular dependency does not cause any problems.

Optional: use Dependo to identify any overlooked module


The image of the tangled mess of modules shown above was created using dependo. As I was refactoring code and adding various require() statement, dependo was helpful in identifying any module not included, either because they had been accidently forgotten or because they had become irrelevant.  dependo can also be installed using npm.

Optional: use JSDoc for creating an API

While I do not particularly like it, as I cannot figure out how to extend it to address my particular needs, I found that jsdoc useful to produce an API for people wanting to use advanced features in creating unusual programming tasks (aka "worlds").  When I started using it, there did not seem to be any easy way to use Sphinx to create such API. I gather that this might no longer be the case ... but it would likely require too much effort to make the change at this point.

jsdoc can also be installed using npm.

Use jshint instead of jslint

A linter can often be useful in identifying potential or real problems with some code. When I started working on this project, the only linter I knew was jslint. jshint is friendlier and more configurable to use, and is my preferred choice. And, you guessed it, jshint can be installed using npm.

Last thoughts

There might very well be other tools that would be better for your own projects but, if you love Python and find yourself not overly enthusiastic at the thought of adopting the Javascript way when working on a project that requires Javascript, you might find that the tools I use match more closely the way you do things with Python.  Or not.



[1] Currently, programs can be written in Python, Javascript, using blockly, or in Python using a REPL.

[2] Language support can mean one of two things: either the programming library for users (like using "avance()" in French as equivalent to "move()" in English, or for the UI, or both. Currently, French and English are implemented for both, while Korean and Polish are only available for UI. Work is underway to provide Chinese support for both.

[3] The tutorial can be found here; you can change the default language using the side-bar on the right. The repository is at https://github.com/aroberge/reeborg-docsThe tutorial is currently available in French, English and Korean, with additional languages in the works.

[4] https://github.com/aroberge/reeborg-howto is a site aimed at creators of advanced tasks for Reeborg's World. It has very little content currently but will have more to be migrated from https://github.com/aroberge/reeborg-world-creation which was written as an online book (a format which I found to be unsatisfactory.)

[5] https://github.com/aroberge/reeborg-api is a documentation site for the API that creators of advanced tasks can use. 

[6] 





Monday, June 18, 2018

Approximate fun


Newest addition to https://github.com/aroberge/experimental


> python -m experimental                                                
experimental console version 0.9.6. [Python version: 3.6.1]             
                                                                        
~~> from __experimental__ import approx                                 
~~> 0.1 + 0.2                                                           
0.30000000000000004                                                     
~~> 0.1 + 0.2 == 0.3                                                    
False                                                                   
~~> # Attempt to use approximate comparison with defining tolerances    
~~> 0.1 + 0.2 ~= 0.3                                                    
Traceback (most recent call last):                                      
  File "<console>", line 1, in <module>                                 
NameError: name 'rel_tol' is not defined                                
~~> rel_tol = abs_tol = 1e-8                                            
~~> 0.1 + 0.2 ~= 0.3                                                    
True                                                                    
~~> 2**0.5 ~= 1.414                                                     
False                                                                   
~~> abs_tol = 0.001                                                     
~~> 2**0.5 ~= 1.414                                                     
True                                                   

Monday, November 20, 2017

Now using Firefox for functional/integration testing

55 seconds versus 20 seconds: this is the reason why I am switching from Chrome to Firefox for my automated tests.  I normally wouldn't write a blog post about what browser I use for testing but I found the difference between the new Firefox and Chrome so striking that I thought I should write about it. If you want to know the details, please read on.

To test Reeborg's World, I use two different testing strategies:

  1. I run unit tests of individual javascript functions using tape; I plan at some point to write a blog post explaining why I think that this is the best Javascript testing framework for casual programmers like me who prefer Python over other languages.
  2. I run functional/integration tests using Qunit
The way I use QUnit is to take the existing html file including all javascript and python code, hide all the normal UI by setting the css property to "display: none", insert some additional testing script which set up automatic programming execution in an attempt to test all robot commands and ensure that all existing programming challenges work.  I run these tests using a local Python browser (similar to python -m http.server) which, admittedly, is not the most efficient server. The integration tests *might* run faster if I were to use a different local web server ... but that would only make the difference between Chrome and Firefox even more striking.

For many years, I have been using Chrome as my browser of choice for development. However, after testing the new Firefox version, I am definitely making the change. Here are the numbers, and how I obtained them.  I ran each testing sequence 5 times.

After starting my local web browser, for each test with a given browser, I open a private window, paste the url in the address bar and press return.  Time to load the page until I see the display, as measured by my watch:
  • Chrome (v 62, 64 bits): 12 seconds
  • Firefox (v 58, 64 bits): 3 seconds.
Yes, measuring with my watch is not precise; however it is precise enough to distinguish between 3 and 12 seconds.

After loading the page, the tests start in earnest.  According to QUnit, the time taken to run the tests is:
  • Chrome: between 42 and 43.5 seconds
  • Firefox: between 16 and 17 seconds.



So, including the time to load (measured by my watch), it takes approximately 55 seconds to run the test suite using Chrome, and approximately 20 seconds using Firefox.

This is why I am switching to Firefox.

As I was curious, I also tried Microsoft Edge. While Edge can be used apparently without any problems on my public site, it hangs inexplicably when trying to run tests from my local (Python) server.







Tuesday, August 08, 2017

Reeborg's World: a new version and a book

After more than a year of rewriting, reorganizing and adding features, a new version of Reeborg's World is finally available.  I have done some fairly extensive testing ... but there's nothing like having other users testing it to discover bugs. I'm hoping to eliminate any remaining bugs by September, in time for the new school year.

As I started to write documentation explaining the new features, I quickly realized that there were a lot of things to be described … so many in fact that it has become a book titled Reeborg’s World: a Teacher’s Guide.  The book is not finished yet but it is available online.



Wednesday, May 17, 2017

What if range did not exist?

Over the years, various proposals for new syntactic constructs have been put forward to supplement or replace the range() function for looping over integers. Some of them have been documented in PEPs, whereas various others arose during discussions on the python-dev, comp.lang.python, and python-ideas lists.


This lead Guido van Rossum to write in 2005:




I don’t think that anyone can reasonably disagree with this assessment, even if I must admit to have suggested something somewhat related not too long ago on the python-ideas list.


Nonetheless, can we imagine what would have happened if range() had not been invented so early in Python’s history?


Consider a situation, in an alternative universe, where Monty, initially known as the Monty Python Programming Language, has evolved without guidance from a BDFL. Instead, through the wisdom of the crowds, and by sheer serendipity, it has grown into something that strongly resembles Python, except that there is no range() function - or, at least, not yet.  Please note that any reference to actual Python related discussions are made simply as a starting point for people interested in exploring what really happened, as opposed to what we invented below. Also, the fictitious names chosen below have been added in after I wrote the invented dialogues, with no connection whatsoever to any living person other than me - save for one obvious person who never actually wrote the 3 words I attributed to him.


Excerpts from the Monty-Musings IRC channel logs



# The evolution of the Monty Python Programming Language was known to be greatly influenced by discussions taking place on the Monty-Musings IRC channel.  The logs of these discussions are scattered over the Internet and it can be difficult to piece them together. As a self-appointed historian, I have assembled some relevant contributions found in the IRC logs; these discussions have taken place over many years. You will find these below, together with some added comments which will hopefully be useful to understand the historical context.


The_OP: I find myself often iterating over the integers.  Each time I end up writing an iterator to do so.  I think it would be useful if such an iterator could be included as a builtin in Monty, so that we could write something like:
   seq = [x for x in integers(n)]
and this would give us a list with integers from 0 to n-1.


The_Self_Appointed_Gate_Keeper: New builtins should only be added when there is a strong case to be made for it. Given the very weak motivation for this addition, there really is no point in continuing this discussion.


# As usual, everyone ignored comments from The_Self_Appointed_Gate_Keeper and the discussion continued. Nonetheless, since The_Self_Appointed_Gate_Keeper maintained such a long and consistent presence on the Monty-Musings IRC channel, we felt it necessary to mention this contribution as a typical example.


The_Mathematician: what you refer to as “integers” should really be named “Natural numbers”, although this would leave some ambiguity as to whether zero is included or not. Furthermore, integers are an infinite set; what you propose to have here is a half-closed interval [0, n), which should not be confused with a half-closed interval (0, n].
You should really be more careful when you use mathematical terms so as to avoid confusion.


The_Minimalist: Why not simply turn integers into iterables so that we could write, for example:


seq = [x for x in 5]   # seq = [0, 1, 2, 3, 4]


# Reference SPAM 276.   For those unaware of it, the normal process by which changes are made to Monty involves the writing of a Standard Proposal for an Addition to Monty, usually simply referred to as a SPAM.


The_Proofreader:  Turning integers into iterators would likely lead to potential problems since something like


x, = 1


would become syntactically valid.I really think we should avoid introducing syntax that is valid and can lead to very different results based to the addition of a possibly single misplaced comma.


# This observation likely contributed to the rejection of SPAM 276.


The_Zen_Fence_Sitter: The proposal needs to follow the “Explicit is better than implicit” principle, and The_Mathematician makes a good point.  I suggest instead to name this function sequence_of_integers_on_a_semi_closed_interval(n).  However, this would still leaves some ambiguity as to which end is closed and which end is open.   And furthermore, following “Special cases aren't special enough to break the rules”, I am not sure if the use case warrants the introduction of a new builtin.  Then again, given that “Although practicality beats purity”, perhaps there is a good case to be made for this addition.


The_Practical_One:  Given that slices already are half-closed, half-open, following Monty’s convention, it should be clear that it is the upper-half that is open.  However, I would make the suggestion that it should be possible to start at something else than zero. This could be written as semi_open_interval(lower, upper) when the lower argument is non-zero -- although I am not too keen on such a long name.


The_Proofreader: The comparison to slices leaves out the fact that a “slice” notation with a single index, like [n], refers to a single item.  Thus, with a single argument, the meaning of semi_open_interval(n) cannot be obtained by using a parallel with the slice notation. In fact, it would lead to confusion given that when only one argument is used, it is meant to represent the _second_ argument of two, with the first one having the value 0 by default, which would be very unusual for a Monty builtin.


The_Unusual_Syntax_Enthusiast: What about using some standard mathematical notation like
 for x in [lower, upper)


# At this point, both The_Practical_One and The_Proofreader jumped in and pointed out the risk of confusion with the notation already existing for lists and for tuples, and the problems associated with parsing more complex expressions with parentheses. This lead to a rather long discussion between the three of them, with a few others occasionally  joining in, unable to resist adding comments to something clearly irrelevant.


The_Mathematician: I think that The_Unusual_Syntax_Enthusiast has a point. In the general situation, the solution is simple.  There are only 4 cases worth considering and, as Knuth has shown many years ago, they can be written as follows:


for x in $[s,e]$:
for x in $(s,e]$:
for x in $[s,e)$:
for x in $(s,e}$:


This does not require any new keyword or builtin function, just a small change to Monty’s syntax to add $ as a useful symbol. Anyone with even the most basic knowledge of \TeX or \LaTeX would be able to quickly grasp the meaning of each of these 4 cases; it’s hard to think of anything that could possibly be more readable, especially if you keep your variable names short like we do in Mathematics.


By the way, it would be so much better if keywords would be prefixed by a backslash, like \for and \in as it would remove any limitation when choosing variable names as it is, most unfortunately, sometimes completely unavoidable to use multi-letter variable names. Furthermore, it would ensure total backwards compatibility when introducing new keywords. I cannot believe I would be the first one to suggest this obvious improvement to Monty.


# The_Mathematician, whose research on the Riemann Hypothesis had been at a standstill for years and had joined the Monty-Musings channel to maintain the delusion that she could contribute something useful to an important project, left the discussion at this point and, if rumours are to be trusted, went off to work on an alternative implementation of Monty done entirely using TeX macros.


The_Unusual_Syntax_Enthusiast: What about using a simple new notation like  
for x in [lower .. upper]


# A similar syntax had been mentioned by someone else before. Again, as was often the case following a suggestion from The_Unusual_Syntax_Enthusiast, a pointless and long discussion followed. Reporting on this discussion would not contribute to the understanding of what lead to the final result.


The_Teacher: I do like this basic idea of having such a built-in iterator.  When teaching Monty to beginners, I often use something like
for number in 0, 1, 2, 3, 4:
   print(number)


This is flexible enough and works for short sequences of numbers, but is not very useful for longer sequences.  However, instead of a builtin with a meaning that is not immediately obvious, what about having something like


for 0 <= number < 4:
  print number


It is both explicit and highly adaptable to other types of sequences. Its meaning would also be clear to my students.


# Historical note 1: A similar proposal gave rise to SPAM 284


# Historical note 2: the use of print both as a keyword and as a function in this contribution by The_Teacher helps us to identify the period at which it was made.  Monty went through what is now referred to as The Great Schism where the language originally known as the Monty Python Programming Language, and later referred to simply as Monty, introduced some backward incompatible changes, the most visible of which was removing print from the keyword list and making it a function. For a while, people referred to the two incompatible versions as Monty (the original version) and New Monty; this eventually changed to Monty Classic (the original version) and New Monty became referred to simply as Monty.  
During the period of upheaval, some rather heated discussions took place which lead some people to describe the Monty-Musing IRC channel as Monty Python’s F-ing Circus. A small troupe of British comedians adopted a sanitized version of this name, and based their comedy act on short plays with nonsensical dialogues, no doubt inspired by the Monty-Musings IRC channel discussions. To this day, the Monty Python’s Flying Circus has essentially disappeared save for a tiny cult following, unlike the ubiquitous Monty programming language.


# Historical note 3. Monty programs are easily recognized by their “.my” extension. Monty enthusiasts, aka Montinistas, have been known to name packages meant to be shared widely with a name also starting with “my”, enjoying the apparent contradiction of having something named “My…” and meant to be shared with all.  For example, one of the best known of such packages is Mygame, the famous gaming framework; another one is Mycroft (https://github.com/MycroftAI), an open source artificial intelligence package.  It has been rumoured that Mynecraft’s name has been inspired by Monty’s tradition. Thus, it is understandable that Montinistas, from both sides of the Classic vs New debate, jointly became upset when Mypy (http://mypy-lang.org/) became public as it was seen clearly to be an attempt to do something perceived as going against the spirit of Monty and with a clear indication that it was not meant to be used except by a small elite group -- this, even though its name started with “My”. Eventually, a united front against Mypy, seen as a common enemy, contributed to the now near-unanimous adoption of New Monty as the true successor to the original Monty Python Programming Language.


The_Unusual_Syntax_Enthusiast: What about using an even simpler new notation and drop the square brackets  
for x in lower .. upper


# Since this was clearly never going to be considered seriously, another very long and pointless side discussion ensued.


The_Grammarian: While the latest suggestion by The_Teacher is interesting, it does not conform to the established pattern which, in its simplest form starts with  


for_stmt: 'for' looping_variable ...


whereas what is proposed does not follow. This could lead to some confusion and would require some non-trivial changes to the parser.


The_Unknown_Coder: What about something like this?




# According to the people we’ve been able to interview, The_Unknown_Coder had the unique habit of including screenshots of the code he ran, instead of using text like all other Montinistas. Since the Monty-Musing IRC Channels logs only preserved the text, we are unable to confirm that what we are presenting is faithfully accurate. We can only guess as to what code was submitted based from comments of other contributors and some minimal recollections from people we interviewed.  There is no evidence that The_Unknown_Coder made any positive contributions to Monty. However, during the discussion that we are documenting, The_Unknown_Coder did make several interventions which seemed to garner some interest from at least one respectable Montinista, even though there is little evidence that those musings had any influence on the final outcome.


The_Grammarian: What you are proposing would still require some difficult adjustments to the parser. It would be much better to use a new keyword instead of “in”, perhaps something like “between” or “over”.  


The_Self_Appointed_Gate_Keeper: New keywords should only be added when there is a strong case to be made for it. Given the very weak motivation for this addition, there really is no point in continuing this discussion.


The_Minimalist: This suggestion, which requires one to write the looping variable twice seems wasteful compare to the suggestion from The_Teacher.  


What about using a bare slice notation?


# Historical note: this last suggestion from The_Minimalist, and the discussion that followed, gave rise to SPAM 204. The fact that a suggestion by The_Minimalist gave rise to a SPAM was very unusual and is worth noting.


The_Unknown_Coder: (# Apparently in reply to The_Grammarian, although we cannot confirm that the following image reflects the code shown in the IRC channel.)
What about something like this instead, where the keyword is meant to be a short form of in_sequence?




The_Grammarian: That would work.  And I can see how your suggestion would naturally include both types of half-closed intervals, as well as open or closed intervals.


The_NASA_Engineer: In our rocket_launch.my script, we have the need for a sequence in reverse order. Could the proposal be made to accommodate this?


The_Old_Timer: We introduced some syntax to handle this case many years ago; it would be


for i in reversed(half_close_interval(n)):


The_OP: I would prefer a single iterator to handle all cases. Perhaps, like we do for slices, we can use -1 as an optional third argument, to indicate that we are using a reverse order.


The_Dutch_Guy: Call it range.


# Note: we dug through all the available records and, as far as we could tell, this terse statement, seemingly coming out of nowhere, was the only contribution from The_Dutch_Guy to this discussion.  It was also the only time that the word “range” was mentioned.


The_Unknown_Coder: My code can handle this case quite naturally.




The_Practical_One: Actually, using a third argument could allow one have integer sequences with steps greater than 1:


for i in half_close_sequence(0, 5, 2):
    print(i)
Which would print the first 3 even integers.


The_Unknown_Coder: My code can handle such cases naturally as well:




The_English_Litt_Graduate: I don’t like it. There are too many symbols and not enough words.


# The discussion continued for many more months but, as far as we could tell, nothing of any significance was added until a suggestion to write a SPAM was made.

The_Friendly_Dev: Dear OP, I think this discussion has been fruitful and deserves to be recorded.  Please, write your idea following the template for a Standard Proposal for an Addition to Monty. When you have finished, upload your SPAM to the DorkCentre repository and post an announcement on 4chan asking people to vote. A month later, we’ll do a vote count, with a weight given to the various 4chan boards following the historical precedent established before most people were able to access 4chan; some people refers to this as Monty’s Electoral College. This vote will decide if your SPAM is accepted or rejected.


Oh, and make sure to submit in triplicate a signed Hacking Agreement for Monty, which we require for anything that someone contribute which may be incorporated in Monty.. Submitting a HAM is required before a SPAM can be made public and a request for votes is posted.



The_OP: Is it wise to ask known trolls to vote on a SPAM like this?


The_Old_Timer: Look, this is how we’ve always proceeded so far, and it has worked out well. You don’t want to have a Committee decide on what to add to the language, otherwise you’ll end up with things like SimpleBeanFactoryAwareAspectInstanceFactory as typical names in the standard library. At the other extreme, you don’t want to have a single person decide or you might end up with something like the Oystr language.  No, democracy using the 4chan weighted Board Electoral System with Trolls  is clearly and tautologically the BEST  way to decide on improvements to Monty. Just trust the wisdom of the crowds. In fact, some countries have adopted a similar practice, also calling it an Electoral College.



The_Friendly_Dev: One more thing, The_OP.  Make sure to include any contribution made by The_Dutch_Guy in your SPAM, however insignificant that contribution seems to you. For some reason, whenever The_Dutch_Guy writes anything, the folks on 4chan get really interested in the outcome and vote in large numbers, all converging towards a sensible solution. Nobody knows why, but it is so striking that it has become immortalized in the Zen of Monty.


# After a hugely popular and positive vote on 4chan, range() became a Monty builtin.