RSS: Syncing Inoreader and Newsboat via. GitHub

Web-based RSS readers are convenient for being a complete online service, but local RSS readers can offer their own advantages. So if you’re looking to get the best of both worlds, here’s my solution.

I use Inoreader as my cloud-based RSS reader, and Newsboat as my local RSS reader.

In summary, the whole thing works using three things:

  • Inoreader’s OPML subscription feature,
  • Newsboat’s OPML export feature (which I periodically run), and
  • Github to host OPML files.

However, this scheme has a problem: I can’t structure my subscriptions! Newsboat only exports a single OPML file, and OPML doesn’t support tags (or similar constructs), so all that structure is lost when Inoreader reads the OPML file.

To solve that problem, I also wrote a script to separate the original OPML file into separate OPML files (one for each tag).

So how does everything work?

(Note: I assume basic familiarity with Inoreader, Newsboat, Git, GitHub, and automation/scripting.)

The OPML file format

OPML is a file format that contains a list of web feeds. These are often used to export/import RSS subscriptions between different RSS readers.

For example, if you’re using Feedly and you one day decide you want to switch to Inoreader, you can download all of your subscriptions from Feedly to your computer as an OPML file, then upload that file to Inoreader. Inoreader now has all your Feedly subscriptions.

Here’s an example OPML file:

<?xml version="1.0"?>
<opml version="1.0">
  <head>
    <title>newsboat - Exported Feeds</title>
  </head>
  <body>
    <outline type="rss" xmlUrl="http://simshadows.com/feed.xml" htmlUrl="http://simshadows.com//" title="Sim's Blog"/>
    <outline type="rss" xmlUrl="http://kuvshinov-ilya.tumblr.com/rss" htmlUrl="http://kuvshinov-ilya.tumblr.com/" title="Kuvshinov Ilya"/>
    <outline type="rss" xmlUrl="https://xkcd.com/rss.xml" htmlUrl="https://xkcd.com/" title="xkcd"/>
    <outline type="rss" xmlUrl="https://googleprojectzero.blogspot.com/feeds/posts/default" htmlUrl="https://googleprojectzero.blogspot.com/" title="Google Project Zero"/>
  </body>
</opml>

Newsboat’s subscriptions storage and OPML export

Normally, your Newsboat subscriptions are stored in the ~/.newsboat/urls file. Mine can be found here.

To export everything into an OPML file, run the following command:

newsboat -e > ~/subs.xml

My exported OPML file can be found at https://github.com/simshadows/sims-dotfiles/blob/master/dotfiles/newsboat/autogenerated-opml/ORIGINAL.xml.

Inoreader’s OPML subscription feature

When you add an OPML subscription to Inoreader, it periodically pings a URL containing an OPML file to monitor it for changes, which keeps your Inoreader subscriptions synchronized with it. This is literally the same basic principle in which RSS works!

Here are my OPML subscriptions at the time of writing:

My OPML subscriptions in Inoreader.

Each OPML subscription in Inoreader is associated with a particular tag.

Hosting the OPML file on Github

In order to use Inoreader’s OPML subscription on our Newsboat OPML export, we need a way to host the OPML file on the internet. This is where GitHub comes in.

With Github, just git push your file up to your public repository, and grab the URL to the *RAW* file and subscribe to it with Inoreader. Every time you update, git push again and Inoreader automatically updates your Inoreader subscriptions!

You can find my raw OPML file at: https://raw.githubusercontent.com/simshadows/sims-dotfiles/master/dotfiles/newsboat/autogenerated-opml/ORIGINAL.xml

Splitting up the OPML file by tag

As I mentioned before, you lose the tag structure from the ~/.newsboat/.urls file if you just use the raw OPML export from Newsboat, and I solved the problem with a script that separates the original OPML file into separate OPML files (one for each tag).

All the script does is:

  1. Parse the ~/newsboat/.urls file for tags.
  2. For each tag, make an exact copy of the original OPML file, except remove the lines containing subscriptions that don’t belong in the file.

For example, if we start with the following ~/.newsboat/urls file:

https://www.nasa.gov/rss/dyn/breaking_news.rss "Science"
http://www.folk-metal.nl/feed/ "Music"
https://myanimelist.net/rss/news.xml "Weeb"

And we the following OPML file:

<?xml version="1.0"?>
<opml version="1.0">
  <head>
    <title>newsboat - Exported Feeds</title>
  </head>
  <body>
    <outline type="rss" xmlUrl="https://www.nasa.gov/rss/dyn/breaking_news.rss" htmlUrl="http://www.nasa.gov/" title="NASA Breaking News"/></body>
    <outline type="rss" xmlUrl="http://www.folk-metal.nl/feed/" htmlUrl="http://www.folk-metal.nl" title="Folk-metal.nl"/> 
    <outline type="rss" xmlUrl="https://myanimelist.net/rss/news.xml" htmlUrl="https://myanimelist.net/news?_location=rss" title="News - MyAnimeList"/>
</opml>

To generate an OPML file for the Music tag, we simply copy over the original OPML file while deleting all unrelated entries, like so:

<?xml version="1.0"?>
<opml version="1.0">
  <head>
    <title>newsboat - Exported Feeds</title>
  </head>
  <body>
    <outline type="rss" xmlUrl="http://www.folk-metal.nl/feed/" htmlUrl="http://www.folk-metal.nl" title="Folk-metal.nl"/> 
</opml>

It’s really that simple! Just subscribe to each separate OPML file on Inoreader.

My implementation is technically actually split between two scripts:

However, I wrote these scripts to fit my repository’s style (and the code’s not very robust), so I suggest you write your own.

You can also find my OPML files for each tag here: https://github.com/simshadows/sims-dotfiles/tree/master/dotfiles/newsboat/autogenerated-opml

Eating Crisps with Chopsticks

Potato crisps (and similar snacks such as cheese twisties and corn chips, which I will refer to blanketly as crisps) are often greasy or otherwise messy to eat without utensils and other tools. Sure, you could just decide to not eat them while doing your term paper or playing videogames, or you could just suck it up and resign to the completely avoidable fate of getting your keyboard, mice, and books covered with food grease. Or, you could actually use a practical solution.

Chopsticks are long and can reach deep into the bag. They’re nimble and have the ability to pick up large crisps, or bunches of various-sized crisps as needed. They make minimal surface contact with the food, but even if the ends get greasy with seasoning, simply run a single segment of toilet paper down the length of the chopsticks twice per stick (assuming you use smooth reusable chopsticks). Now, they’re clean enough to rest back down to be used again for the next bag.

Chopsticks are of course not just limited to crisps and your more “traditional chopstick foods” such as ramen and dumplings. They’re great for anything else bite-sized: potato chips, chicken nuggets, Turkish pide, and sometimes even chicken wings (although those tend to be a lot more challenging to clean up around the bone), and many others are completely practical to eat with chopsticks.

Other solutions certainly exist and can also be effective (usually depending on the nature of the snack). But for me, although it can look a bit silly, nothing beats the fine control, swiftness, and cleanliness of chopsticks.

Epic Fail #3: Copy-Pasting Code With Unicode Quotes

For Web Application Security class last year, most of the final exam was an open-book, open-internet practical: we were given a remotely hosted web application to play with, and we had to obtain flags. No flag meant no mark.

My prepwork involved making a PDF document using Latex containing all the written notes I needed. This was of course supplemented by other resources such as my archive of homework solutions, lecture slides, textbooks, etc. A key component of that Latex document of course was code that could be copy-pasted when needed.

The problem with that code, however, is that it’s not like a program you’re meant to compile and run locally, otherwise a compiler or interpreter would’ve caught the issue and issued the relevant warnings for the problem, allowing it to be resolved quickly. Instead, the code is often pasted directly into input text fields of a web page like so:

Code pasted into an input field on a web page on Chrome.

Or, the significantly better method of using Burp Repeater and other such tools:

Burp Suite Repeater example screenshot.

In the above screenshot, the raw input data can be observed and modified here:

Burp Suite Repeater example screenshot.

After sending off the input data to the remote server, feedback produced by the server may be limited and you may have to infer the web application’s state from minimal information. After all, web servers and applications are ideally designed to withstand attacks from the outside which look to coax the app/server into doing something to the attacker’s advantage. Intentionally hiding unnecessary information about the system is one of many tricks defenders use as part of the overall defense strategy.

In reality, the amount of relevant feedback emitted by the web server/application can vary significantly, depending on many aspects of the system. However, the fact still remains: A lot of the time, you’re swinging in the dark until you catch something. You may catch plenty, you may catch none, or you may even catch bait or misleading responses.

In my case, despite having revised and practiced extensively before that final exam practical, I got zero flags. Even more painful was how I spent the two hours trying every trick and variation I knew, and feeling like I knew exactly what the solution should be for all flags that didn’t require knowledge of other flags, yet nothing did anything interesting. I kept thinking I missed some detail about the web application and continued to think along those lines.

After the exam was over, I got a colleague to show me a flag. She got two of them instantly. (I think the application was taken down before we could try more flags.)

That afternoon, I found my text document full of payload code I made during the exam. A lot of the quotation marks were found to be Unicode, as an artifact of my PDF document compiled from Latex. All my payloads were based off text copied from that PDF document. Although I can’t confirm, that was most likely the reason my payloads failed to make the web application budge one bit. If I just used ASCII quotation marks, I would’ve gotten at least a few flags.

Unicode Quotation Marks? What?

By Unicode quotation marks, I’m referring to the quotation marks that are not part of ASCII.

The ASCII quotes (including the grave mark) are:

Hex    Unicode   Raw   Description
----------------------------------------------------
0x22   U+0022    "     QUOTATION MARK
0x27   U+0027    '     APOSTROPHE
0x60   U+0060    `     GRAVE ACCENT

The quotation characters above are the ones that are typically intended for the computer to parse with special meanings that are important in all aspects of a programmer’s work.

The following is a subset of what I consider to be the “Unicode quotation marks”:

Unicode   Raw   Description
----------------------------------------------------
U+2018    ‘     LEFT SINGLE QUOTATION MARK
U+2019    ’     RIGHT SINGLE QUOTATION MARK
U+201C    “     LEFT DOUBLE QUOTATION MARK
U+201D    ”     RIGHT DOUBLE QUOTATION MARK

(Note: Technically, ASCII is a strict subset of Unicode blocks, hence ASCII characters are also Unicode characters, but Unicode characters are not all ASCII characters.)

These “Unicode quotation marks” are intended to be more typographically accurate than ASCII quotes (which appear straight compared to the curliness of Unicode quotes). They are not typically meant to be interpreted by computers in place of Unicode quotes.

In my case, the ASCII quotes I used in the Latex document were converted to Unicode quotes in the compiled PDF. Word processors (such as Microsoft Word) will often similarly convert quotes. Other applications (such as email) may also convert quotes. All of this is done to make the final document visually pleasing and typographically correct (much to the detriment of computing professionals out there).

You may have also noticed that this blog post’s quotation marks may have been converted into Unicode quotation marks (except in code blocks). Assuming I haven’t changed things too much since the time of writing, these blog posts are sourced from files written in Jekyll Markdown, and here, I use ASCII quotes.

Further reading:

Recommendations

Even beyond things like rendered PDFs, Microsoft Word, and HTML web pages, you may run into the issue in simple plaintext files (such as if someone simply copy-pasted text in from Microsoft Word or a PDF, like during my web application security exam). Unicode quotes can come in from anywhere when you least expect it.

This is one of those things where the best you can do is be aware and have it at the back of your mind, and hopefully this blog post has given you that awareness. Next time you run into a cryptic problem with seemingly no obvious reason, Unicode quotes may be worth considering.

Beyond Unicode Quotes

It really doesn’t take much to go beyond and find more problems related to using different characters that look visually similar (or the exact same). For instance, a popular example of this problem set is the “GREEK QUESTION MARK” (U+037E)”. This character is visually similar to the semicolon character (;), and is often used as an example of a prank that can be used on programmers (such as this Stack Overflow post).

In the context of security, the exploitation of such a mechanism is called a homograph attack. IDN homograph attacks for instance can be used to trick users into believing they’re browsing www.apple.com when in fact they are browsing xn–80ak6aa92e.com (that link is not malicious at the time of writing, and was made to harmlessly illustrate the dangers of this IDN homograph vulnerabilities).

Further Reading:

As for me, I took a supplementary exam for web application security class several weeks after the original exam. Needless to say, I made doubly sure I had properly formatted (with ASCII characters) code the second time around. My unicode quotes problem was no more.

VirtualBox Linux VM Development Environments

I’ve chosen to do the majority of my development work in a VirtualBox VM running an ultra-lightweight configuration of Arch Linux, with the VM typically running fullscreen or in a large window. All my files are in the virtual disk, I use a text editor inside the VM, and all of my tools are in there.

It works beautifully for me, but for many, native development is suitable enough. Running natively has the distinct advantage of performance, tools for practically all popular needs exists on all major platforms, and you often won’t need to mess with the environment too much anyway once you’re established. Even the barrier to developing for Linux systems on Windows is largely lifted thanks to Vagrant, Docker, or possibly even WSL.

And for some, native development will be the most sane option, particularly if hardware access is required as GPU access can be difficult and I/O may be poor. For instance:

  • Game developers often have heavy workloads that need the GPU, and games are often developed for Windows,
  • Machine learning and other scientific workloads often benefit greatly from GPU acceleration, and
  • I personally have trouble accessing any USB port from within any Linux VM. (This issue may be fixed though.)

So if that’s you, you know who you are. There are solutions (such as this), but I haven’t had a chance to play around with them yet.

However, there are many interesting advantages that make a VirtualBox Linux VM well worth considering if it works for your workload.

Note: I’ll be talking more on the pros and cons of the virtualization side of things rather than diving too specifically into my Linux setup.

Advantages

Advantage 0: VirtualBox and Linux are FOSS

Not really a point I’m arguing for (hence “Advantage 0”), but it’s most certainly a plus!

Advantage 1: It’s literally a Linux system that just bloody works.

For Windows users, I think this is the most important point. With a VirtualBox Linux VM, there is no need to wrestle with your native OS to get something working, and you dodge a lot of the weirdness and tech-acrobatics that can be required. Yes, it can work if you tried hard enough, but sometimes it’s nice to simply not have to go through that.

On top of that, you’ll be working on literally Linux. If you’re a Windows user taking computer science classes in college and your class requires you to program in C on a Unix-like system, this can save many headaches while at the same time familiarizing you with the same environment everyone else is using.

Or if you, like me simply prefer working in Linux but for various reasons have to install Windows or Mac OS natively on your machine (such as to game on Windows or to reap other benefits Windows and Mac OS have over Linux), you can still feel right at home.

Advantage 2: Portability

Not only does the VM work, but it generally works wherever the latest version of VirtualBox is supported.

This is particularly awesome if you have multiple machines and you want to work on all of them. In my case, I have a Windows-based gaming desktop machine and a Macbook Pro, and I simply copy the VM files between them as necessary. (I only bother copying the VM after major system changes thanks to git.)

Everything moves, so the result is that it works and looks practically exactly the same, everything from your text editors to your tools, your files, and your perfectly arranged directory structure. Everything works exactly how you like it. No need to somehow match certain settings between your computers, no need to resolve mismatched compatibility. And with the massive amount of customizability and choice in Linux-land, all those tweaks you made? Yep. They all stay with your VM.

Reformatting and reinstalling your host OS is also made simpler since you don’t have to bother as much about your development environment; just reinstall VirtualBox and copy your VM back in. I’ve had to do that to resolve sluggish performance on my Mac.

Advantage 3: VirtualBox Snapshots

The state of your entire machine can be versioned so you can go back to a different point at any time.

To me, this has two broad but important implications.

First, it allows you to recover faster from a broken system. However, this assumes that you create snapshots regularly. In my case, I generally make snapshots between major system changes, after successful whole-system updates, or major /home directory structure changes. Since I use git and put repositories on GitHub quite liberally, I don’t really bother too much if I lose changes within repositories after being git pushed.

Second, snapshots enables experimentation, which I think is important for users learning how to use Linux (such as myself!). Snapshots can even be taken while the machine is running, so simply taking snapshots at key points will allow you to try new things, observe the effect, and easily recover if you end up breaking your system.

Snapshots do take host disk space though, so I only keep two snapshots into the past (deleting old ones), and I don’t “fork”.

Advantage 4: Isolation

This can be a huge boon if you like tinkering and exploring, in case you mess something up or if you accidentally run malware or get hacked. Everything that happens inside the VM stays in the VM. If the VM crashes, it probably won’t mess up your host OS.

(Do remember though that this isolation is not perfect. It does however make things increasingly difficult for attackers.)

(Also, your VM may not be guarded against consuming your computer’s resources, so your computer may freeze if you hit it too hard. That should be obvious, but I’d like to mention that for clarity.)

Advantage 5: A VirtualBox VM Encourages Experimentation

I’ve already touched on this in my discussion of snapshots, but all of these points so far are great for experimentation. I don’t think I need to explain any further.

Advantage 6: Linux is Awesome

Depending on who you ask, the fact that you’re running Linux can be a huge advantage or disadvantage.

In my case, I absolutely love using Linux, so I’ll list it as an advantage. (I’ll probably write more on this in the future, so I’ll leave it at that for now.)

Disadvantages

Such awesomeness unfortunately comes with a few major downsides that can make or break the entire experience.

Disadvantage 1: Hardware Access Difficulty (especially GPU and USB)

I’ve already mentioned this earlier: while the system works excellently from within, I’ve had major difficulties getting the VM to interface and work with USB (VirtualBox USB passthrough has been unusably unstable for me), and I suspect GPU isn’t so smooth sailing either with VirtualBox.

However, there are probably better solutions out there (or maybe VirtualBox’s USB drivers are fixed). I haven’t really looked into it so if you find something, I’d love it if you could let me know!

Disadvantage 2: Performance

It should be obvious that VMs come with performance penalties. Your VM will be eating up a portion of your CPU, memory, and potentially your entire graphics card.

However, I find my experience to be pleasantly smooth (no noticeable performance penalties) with the following settings:

  • 2048-4096 MB RAM assigned to the VM,
  • Allow the VM to use as many cores as there are cores on your computer and with no execution cap,
  • 256 MB Video RAM assigned to the VM, and
  • Enabled 3D and 2D acceleration on the VM.

These settings work perfectly on my late-2013 Macbook Pro with 8GB of RAM (and it even supports me using both a Linux VM and a Windows 7 VM concurrently!), but it may not work on your machine, especially if your VMs aren’t configured to be as lightweight as mine. As such, your mileage may vary.

If you want ballpark numbers though, my (untested) advice is to have a minimum of 8GB of RAM in your computer in order to be comfortable using a VM. Otherwise, any decent 2-core laptop manufactured after 2012 will probably serve you fine.

(Or, you could just test performance on your machine anyway since VirtualBox and Linux are free :)

Disadvantage 3: The VM requires extra disk space

My Linux VM takes 34.86 GB at the time of writing, but it can be much smaller. Within the Linux VM, I’m only using 20.9 GB.

That consumed space is barely felt in my 256 GB Macbook though, and I suspect that might be the same with many others users.

As for the additional 14.0 GB of virtual disk bloat, I suspect it comes from several places:

  1. Having to install the operating system,
  2. Having to install a lot of standard tools that may already exist in the host, and
  3. The virtual disk file is probably not filesystem-aware, so deleting files may not actually shrink the virtual disk file.

There is a way to lessen the impact of point #3 by “compacting” the filesystem and zeroing unallocated blocks, but that can be a hassle. I don’t even bother anymore.

(For reference: My Windows 7 VM takes 51.28 GB, and it’s basically a fresh install!)

Disadvantage 4: Your entire VM is accessible

Generally, sensitive data is protected by preventing users’ direct access. I’m not just talking about your business documents and various embarrassing selfies; the software itself and various cryptographic secrets can be stolen or tampered. Having a full VM sitting on disk on your host OS and accessible through your user (as opposed to needing administrator/root access) opens you up to a variety of potential attacks. For instance:

  • Malware may scan your files and steal RSA private keys from your unprotected VM,
  • Malware may install rootkits in the VM that can be hidden from detection, and
  • If someone steals your VM, they’ve practically stolen your computer!

A possible solution is to enable virtual disk encryption. This should make it exceptionally more difficult to “smash-and-grab” from the disk, though it’s not perfect since keyloggers and other techniques can still be used to circumvent it.

In practice, it may not matter too much as long as you have good security practices; if your user is compromised, it’s probably just a matter of time before they gain deeper access to your machine. However, it pays to be a bit paranoid.

(I’d also like to clarify: You probably use disk encryption with your host OS, but that can only help against physical theft of your computer (and even then, it’s not necessarily perfect). You will still need to encrypt your virtual disk in order to mitigate those attacks I mentioned above.)

Disadvantage 5: It’s practically another system to maintain

Unfortunately, having a VM also means another system for you to maintain. However, that may not actually be that bad. This is a great way to learn Linux!

Disadvantage 6: If it ain’t broke, don’t fix it.

Perhaps everything is working perfectly fine for you developing in your native OS, so it can be difficult to see why you should move to a VM.

However, maybe consider trying out a VM anyway. Trying new tools in general is a great way to learn new things and improve your workflow!

Final Thoughts

Hopefully I’ve provoked some ideas on the possibilities with virtualization. However, you may still be wondering: Why Linux?

After all, the entire GNU/Linux ecosystem is the other major part to consider if you want a VirtualBox Linux VM.

I’ll leave that for another post.

Epic Fail #2: Assuming the function that throws the exception is where the error is in my code

For neural networks class, a major component of the second assignment was to build and train a recurrent architecture. At this point, I haven’t really had a tonne of experience with the TensorFlow framework. Although I’ve implemented some simple networks, I haven’t done something involving recurrent architectures before, so I consulted various tutorials.

With these tutorials, my overall approach of starting with a rough sketch in code I think makes sense when learning a new framework: You build a rough version that seems to represent what you aim to do, then you iron it out to get your first super-basic running version. Figure out what it’s doing, then iteratively build on top of it. Ideally, this would mean going back to the framework documentation and seeing if all the pieces work as you expect.

Unfortunately, somewhere along the way, I had the brilliant idea of blindly using a TensorFlow function without reading the documentation, and continuing on to assume it to be correct. Testing raised an exception.

And on debugging that exception, I made the assumption that if a function call doesn’t raise an exception, then I used the function correctly.

Working from these assumptions, I wasted a lot of time attempting various things and reading different tutorials. With nothing working, I then moved on to reviewing all the relevant theory from the lectures to ensure I completely understood everything. But even after that, I couldn’t figure out what was wrong with the code, so I spent more time reading various articles on the design of the TensorFlow framework. Nothing gave me an answer, which left me beyond frustrated.

But as it turned out, the function call raising the exception was fine. The problem was that I used another function incorrectly in an earlier part of the code. Despite using that function incorrectly, no exception was raised until later on.

The Problematic Code

In the version before fixing the error, I had the following two lines as part of the graph definition code:

lstm = tf.contrib.rnn.BasicLSTMCell([BATCH_SIZE, WORD_COUNT])
rnn_outputs, states = tf.nn.dynamic_rnn(lstm, input_data, dtype=tf.float32)

When run, the first line seems to run fine while the second line raises an exception that seems unrelated to my own code, and rather a bug in TensorFlow:

Traceback (most recent call last):
  File "train.py", line 41, in <module>
    imp.define_graph(glove_array)
  File "/home/simshadows/git/cs9444_coursework/asst2stage2-sentiment-classifier/implementation.py", line 154, in define_graph
    rnn_outputs, states = tf.nn.dynamic_rnn(lstm, input_data_expanded, dtype=tf.float32) # , time_major=False
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/rnn.py", line 598, in dynamic_rnn
    dtype=dtype)
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/rnn.py", line 761, in _dynamic_rnn_loop
    swap_memory=swap_memory)
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/control_flow_ops.py", line 2775, in while_loop
    result = context.BuildLoop(cond, body, loop_vars, shape_invariants)
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/control_flow_ops.py", line 2604, in BuildLoop
    pred, body, original_loop_vars, loop_vars, shape_invariants)
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/control_flow_ops.py", line 2554, in _BuildLoop
    body_result = body(*packed_vars_for_body)
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/rnn.py", line 746, in _time_step
    (output, new_state) = call_cell()
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/rnn.py", line 732, in <lambda>
    call_cell = lambda: cell(input_t, state)
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/rnn_cell_impl.py", line 180, in __call__
    return super(RNNCell, self).__call__(inputs, state)
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/layers/base.py", line 450, in __call__
    outputs = self.call(inputs, *args, **kwargs)
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/rnn_cell_impl.py", line 401, in call
    concat = _linear([inputs, h], 4 * self._num_units, True)
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/rnn_cell_impl.py", line 1021, in _linear
    shapes = [a.get_shape() for a in args]
  File "/home/simshadows/.local/lib/python3.6/site-packages/tensorflow/python/ops/rnn_cell_impl.py", line 1021, in 	<listcomp>
    shapes = [a.get_shape() for a in args]
AttributeError: 'list' object has no attribute 'get_shape'

The problem in the code above is that tf.contrib.rnn.BasicLSTMCell() takes an int as the first positional argument, not a list. (See the __init__ arguments in this page of the documentation.)

The version after fixing the error (simplified) looks like the following:

lstm = tf.contrib.rnn.BasicLSTMCell(HIDDEN_SIZE)
rnn_outputs, states = tf.nn.dynamic_rnn(lstm, input_data, dtype=tf.float32)

An Explanation

With duck-typing in Python, that previous function call didn’t raise an exception. This was because the operations performed with the bad argument value were by chance supported by that bad argument value.

However, having the bad argument work doesn’t mean we can get away with it. The bad function call can break a class invariant, meaning that the state of the object is no longer guaranteed to be valid.

With the state of the object now potentially invalid, the behaviour of operations on or with that object will now be undefined, and can raise weird exceptions that can be difficult to trace back to the bad function call.

To illustrate what this means, let’s look at a simple example.

Example: Incrementor

Consider the following class definition:

class Incrementor:

    def __init__(self, value):
        # value is an int.
        self.value = value

    def change_value(self, value):
        # value is an int.
        self.value = value

    def inc_and_print(self):
        self.value += 1
        print(str(self.value))

Added above in comments are the documented usages of the methods. Particularly, these methods require the argument to be an int.

And now consider the following sequence of calls:

>>> x = Incrementor(4)
>>> x.inc_and_print()
5
>>> x.inc_and_print()
6

Looks good so far. Note that the constructor call Incrementor(4) followed the documented usage by passing 4 as the argument, which is an int.

Now consider the following sequence of calls, continuing on from above:

>>> x.change_value("twelve")
>>> x.inc_and_print()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/simshadows/work/tmp/myincrementor.py", line 10, in inc_and_print
    self.value += 1
TypeError: must be str, not int

The first statement x.change_value("twelve") was a method call that passed a str instead of an int. Despite deviating from the documentation, the method call succeeded because the operations it performed were supported by the str(since there was nothing more than the value assignment self.value = value).

However, x.change_value("twelve") broke an undocumented invariant that the author of the class definition relied on. In particular, the author of the class relied on self.value being an int.

The next statement x.inc_and_print() then raised an exception because it needed to do something which relied particularly on this invariant.

What about statically typed languages?

Indeed, I mentioned duck typing earlier, and it may seem that perhaps this could be solved by instead using a statically typed language such as Java. And indeed, that will help. By having the compiler check on compile time that types are as expected, a great many such errors can be caught at compile time.

However, it’s not a perfect solution because preconditions to functions can go beyond simply type-checking. For example, a function might expect to be passed list with more than 3 elements.

Perhaps I might write another post some day going into more depth on this since this is a rabbithole into software design which goes beyond the intended scope of this blog post.

Update #1: Blog format? What blog format?

So I’ve been thinking a bit more lately on how I want to structure my blog. So far, I’ve been doing far too much long-format almost-stream-of-consciousness writing (with minor editing to rearrange paragraphs) that’s just really difficult to read. Although, as much as I’d like to write a tonne and be precise about everything, perhaps there’s a better way?

Finding and establishing an effective blogging format is also of particular interest since I’m interested in using this as a public learning diary, yet I don’t want to spend too much time writing. I just want to get down and do.

So for a while, I guess my blogging style will shift around while I look for a sweet spot.

And on top of that, I’m also looking into figuring out how the categories should work and how I might organize the whole blog (and the other blogs) to make it easier for people to browse through, especially newcomers.

Page 2 of 3