Star Trek Phone Chargers

In this Fediverse thread about how the most implausible part of Star Trek is that all of the devices just kind of interoperate when we can't even get phone chargers to work right, I got inspired:

Don't use the Klingon one. It'll blast your device with random voltages and then call it weak if it explodes.

The Borg one works with anything but after the first charge, your device will no longer be compatible with any other charger.

The Ferengi one is crap, plus it bills you per minute of charging.

The Romulan one is widely believed to install spyware but nobody has managed to prove it.

The Vulcan one is a featureless gray sphere with a slight flat spot so it doesn't roll. Nobody has any idea how to use it. When asked, Vulcans will respond either implicitly or explicitly that it's obvious and your question makes no sense.

Cardassian chargers have astoundingly terrible ergonomics. It's possible to get one to work with most phones but it's usually not worth the effort.

The Bajoran chargers are just Cardassian chargers with pieces hacked off and the gaps filled with epoxy and duct tape.

They're still easier to use than the Cardassian chargers, though.


#   Posted 2019-10-10 01:28:01 UTC; last changed 2019-10-10 01:30:21 UTC



Sic: Yet Another Mediocre Small Lisp Dialect

Like many bad ideas, this one came from seeking attention on social media. I had envisioned tooting something like this on Mastodon:

Me? Oh, I was just writing Lisp code in C++. As one does.

(dontforgettolikeandsubscribe)

This was the result of a series of realizations I had while learning more about modern C++. Specifically:

1. $ is a valid 'word' character in C++ these days.

That means you can use it in names. So this is valid C++:

const int $20_same_as_in_town = 20;

But since this not widely known, I can use it for all kinds of shenanigans.

2. You can use variadic templates to fake Lisp-style expressions.

As you know Bob, variadic templates are function templates that take arbitrarily many arguments. And since they're templates, their type is also up for grabs.

And as you also know Bob, Lisp-style lists are just linked lists of pairs of pointers where the first pointer holds the value and the second the next pair in the sequence. And Lisp expressions are just lists of expressions where the first value is the function to call while the rest are its arguments.

So if we create a bunch of C++ classes to represent basic Lispish types:

class string : public obj { ... };
class symbol : public obj { ... };
class number : public obj { ... };

plus a common base class:

class obj { ... };

plus a simple C++ class to hold a pair:

class pair : public obj {
public:
    obj * const first;      // Not named 'car'; cope.
    obj * const rest;       // Ditto for 'cdr'.
    pair(obj *a, obj *d) : car(a), cdr(d) {}
}

and a set of overloaded helper functions to convert basic C++ types to Lispish types:

static inline obj* _w(std::string s)  { return new string(s); }
static inline obj* _w(int i)          { return new number((long)i); }
static inline obj* _w(long l)         { return new number(l); }
static inline obj* _w(double d)       { return new number(d); }
static inline obj* _w(obj *o)         { return o; }

we can create a variadic function template that constructs a Lispish list:

template<typename T> obj* $(T o) { return new pair(_w(o), nil); }

template<typename T, typename... Objects>
obj* $(T first, Objects... rest) { return new pair(_w(first), $(rest...)); }

(The first definition of $ works on any call with one argument; the second expands to a function that takes the first argument and recurses on the rest.)

Calling it looks like this:

$("foo", 42, $("add", 2, 2))

which looks Lispish if you squint hard enough. But the resulting list is an actual Lisp-style list.

3. Evaluating functions is pretty straightforward.

A function is just one of the Lispish C++ types. It implements a method named call, which takes an argument list and returns a result:

class callable : public obj {
public:
    virtual obj* call(obj* actualArgs) const = 0;
};

Yeah, yeah, this is an abstract base class. We actually need two types of callables:

class function : public callable { ... }
class builtin : public callable { ... }

function is a function written in Sic. It holds a Lispish list of expressions (also Lispish lists) and evaluates them by calling eval on each of them. builtin holds a built-in function--a C++ lambda (or other function pointer, in theory)--that it calls instead.

eval is the function that evaluates a Sic expression. You give it the expression and if it's a list, it recursively calls itself on each item and collects the results. Then, it calls the first item's call method with the rest of the list as an argument and returns the result. If the argument isn't a list, it just returns it.

So like any good Lisp function, it either does nothing significant or recurses.

4. Also, I can do macros because I hate myself.

As you know Bob--hey, why are you walking away? I NEED YOU FOR THIS RHETORICAL DEVICE, BOB!

Anyway, as Bob over there already knows, a macro is a powerful and elegent way to let programmers mutate their Lisp into a completely different language while introducing subtle and impossible-to-find bugs.

More precisely, It's a function that gets called on the raw, unevaluated argument list, does something with that and returns something else that does get evaluated as a normal Lispish expression. On compiled Lisps (i.e. not this one), it gets called by the compiler.

The thing is, we need macros for system-ish and control-flow-ish stuff. You can't do an if statement if eval is always going to evaluate the THEN and ELSE expressions regardless.

So we do macros by adding the isMacro flag to callable:

class callable : public obj {
public:
    const bool isMacro;
    virtual obj* call(obj* actualArgs) const = 0;
};

(isMacro gets set by the constructor.)

Then we make eval check if it's true. If it is, it calls the function on the arguments first, before they're evaluated, captures the result and recursively evaluates that.

And there you go. Self-modifying code made easy.

(The Scheme community has new and interesting ways to make macros safer and easier to use. I strongly disagree with this. If macros are easy to use, people might start using them, and that's only going to lead to trouble.)

5. Errors are just C++ exceptions

The easy way to handle errors here is to just throw a C++ exception where necessary. We give them a common base class to distinguish them from other types of exceptions, but that's pretty much it.

Need a stack trace? Put a std::vector in the base class and wrap eval or call in a try block that adds the details to it. Easy!

6. Local namespaces are a linked-list of std::map-holding objects

Now that we're actually interpreting code, we need variables. This is pretty simple, right? C++ has std::map which does pretty much everything we need. Just wrap it with a class:

class context {
private:
    std::map<std::string, obj*> items;
public:
    void set(const std::string& name, obj* value) { ... }
    obj* get(const std::string& name) const { ... }
}

And that's all we need--no, wait, there's also a global scope so it needs to fall through to that. So we add a pointer to an outer scope:

    context * const parent;

and make set and get fall through to the parent if it's not local. Easy!

No, wait. set falling through means I have to make defining a variable in a local scope so stuff like this will work:

(let ( (outer nil) )
    (let ( (x 1) )
        (setq outer 42)))

We don't want setq to define a new variable outer in its scope; it should be writing to the existing outer. So we need to make defining variables and assigning to them separate things. We do this by making set throw an exception if it can't find the variable, then adding a define method:

    void define(const std::string& name, obj* value) { ... }

And that... works?

Looks around nervously.

Next, we need to add the context as an argument to Callable::call:

    virtual obj* call(obj* actualArgs, context* outer) const = 0;

and propagate it to eval and whatever else needs it.

Because built-in functions now get a pointer to the caller's context, they can modify the caller's variables. Which is generally a Bad Thing unless we need to write set, which we do. So it's actually a good thing, I guess.

(We also have to write the macro setq--which expands to set--because typing that extra quote is so burdensome. No, seriously, it's a huge pain--the number of times I forgot it is basically the number of times I wrote buggy Sic code.)

We also need this when defining lambdas because (as Bob over there knows), they can access the scope in which they are defined. That is, this:

(defun return-x-f (x) (lambda () x))
(setq fn (return-x-f 42))
(print (fn))

will print 42, because the lambda holds on to the outer function's context.

So lambda (well, its back-end--it's a macro, after all) gets a pointer to the caller's context and stashes it in the function object:

    context *outer;

When the lambda gets called, it creates its context (the equivalent of a stack frame in C++) and sets outer as the parent.

Conveniently, non-lambda functions (ironically-named fun) are just like lambdas except that their outer pointer just points to the global context (i.e. the outermost parent).

Finally, we need to actually look up variables. That ends up being a one-liner in eval:

if (expr->isSymbol()) { return context->get(expr->text); }

And that's all.

7. Oh yeah, I forgot to talk about Symbols

As Bob--hey, where'd he go? Anyway, as Bob knows, Lispish languages have this concept of a symbol, which is different from a string. A symbol is a chunk of text but it represents an internal variable name.

If you hand eval a string, it just gives you the string back but if you hand it a symbol, it'll look it up in the current context and give you the value back instead. Which you already know, because you read the last section. Right?

So in Sic, a symbol class is just an obj subclass that holds a std::string. Except the field has a different name from the Sic string (text vs contents) so that I can't accidentally use one instead of the other.

class symbol : public obj {
public:
    const std::string text;
    explicit symbol(std::string& v) : text(v) {}
};

There's nothing really magical about it except that eval can tell the difference between the two and handles them differently.

Oh, and also, I did a clever thing in the symbol class where I guarantee that there's only ever one symbol for a particular series of characters. This ensures the Lispish requirement that symbols be unique and also lets you test equality in C++ by comparing the pointers with ==.

So it really looks (more) like this:

class symbol : public obj {
private:
    inline static std::map<std::string, symbol*> symbols;
    explicit symbol(std::string& v) : text(v) {}

public:
    static symbol* intern(std::string s) {
        if (symbols.count(s) == 0) { symbols[s] = new symbol(s); }
        return symbols[s];
    }
};

(Basically, I make the constructor private and provide a public static method called intern that calls it, but only if there's not already an instance in symbols. In that case, it first stashes the symbol there before returning it. Otherwise, it returns a pointer to the stashed symbol already.

This all works because Sic types are immutable (barring C++ type abuse, that is).)

The other thing I need to mention is that code like

$("setq", "foo", 42)

that I used above doesn't actually work the way you'd naively expect. The arguments are strings which eval won't look up. We need to make them into symbols.

Unfortunately, C++ doesn't have a symbol type and we already transparently turn C++ strings into Sic strings, so there's not an obvious conversion.

So we make it explicit with a helper function. Which I name $$, 'cuz why not:

static inline obj* $$(const std::string& s)  { return symbol::intern(s); }

So now, we can do explicit symbols like this:

$( $$("setq"), $$("foo"), 42)

Which is only slightly uglier.

(To make this slightly easier, sic.hpp also defines a bunch of global consts that hold pointers to the corresponding functions. This lets you replace the above with:

$(set, $$("foo"), 42)

which is a bit nicer.)

8. read is complex and ugly but uninteresting

So you'll note that at this point (assuming I've also written a bunch of useful built-in functions), we pretty much have a working(ish) programming language. I can do stuff like this:

$(progn,

  $(print, "starting!\n"),

  $(defun, $$("fib"), $( $$("n") ),
    $(if_op, $(le, $$("n"), 1),
      $(list, 1),
      $(if_op, $(eq_p, $$("n"), 2),
        $(list, 1, 1),
        $(let, $( $( $$("prev"), $( $$("fib"), $(sub, $$("n"), 1) ) ) ),
          $(pair_op, 
            $(add, $(first, $$("prev")), 
                   $(second, $$("prev"))), $$("prev") )
            )
          )
        )
      ),

  $(print,
    $( $$("fib"), $(str_to_num, $(third, $$("argv")) ) ),
    "\n")
    )
;

and it works.

Lispish source code is basically just text serialization of its data types--primordial JSON, as it were, so all I really need to do to write and evaluate scripts is a function to parse lists, names and literal types.

In most Lisps, this is called read so I called it that too.

read is written in pure C++ and does pretty much what you'd expect with std::stream and std::string. Boring, in other words. But it works.

I did make one attempt to be innovative and modern and made the comment character # instead of ; because that's more Unixy and you can do the #! thing to launch scripts. Of course, editors still expect ; so I ended up putting back ; as an alternate comment character.

So now you have two comment characters for the price of one. Lucky you.

8. Garbage Collection would be nice but it's too much work.

Unlike most Lispish languages, Sic implements garbage collection by having me suggest that you exit your program sometime before your computer runs out of RAM. After that, object memory is reclaimed very efficiently.

(It would be (sigh) relatively straightforward to create an abstract base class for obj and context that stores all of the instances in a global registry and provides marking for mark-and-sweep, but that's a lot more work than I want to do right now. It's probably easier to just grab the Boehm GC and use that.)

9. It's no longer fun but I can't stop. Help!

So now that I have read, I can split Sic up into a library and a script runner.

The library gets a function called root_context() that creates a global context (i.e. one with no parent) and loads it up with all of the built-in functions. A neat side effect of this is that you can now have multiple Sic instances in your program; just create a new root context for each.

The runner links to the library, calls root_context() and either loads in the script you give it or lets you type in commands. (If you want readline support, rlwrap is available.)

Oh, but it'd be nice to have a unit test framework. So I'll add extra testing builtins but only if the script name ends with .sictest. Which turns out to be much trickier than it looks.

So once that's working, I should probably test most of these functions. But I don't really have proper equality testing so I should add that. And tests. Also, I should write examples. But this example would be much easier if I had cond (plus tests). And cond makes or really easy, so I should add that (plus tests). But or implies and, so I should add that as well (plus tests).

And I really should--

On second thought, it's done now.

10. Screw it. It's on Github now.

If you want to play with the code, it's here. I'm releasing it under the terms of the wxWidgets license, which is basically the GNU LGPL but with less restrictions on using it in your own programs.

Have fun. Or not.


#   Posted 2019-06-22 23:49:10 UTC; last changed 2019-06-25 01:57:31 UTC



A Quick and Dirty Tutorial for Writing Mastodon Bots in Ruby

Note: see the update below.

My most recent project has been this Mastodon bot. Every n hours (where n == 8, for now) it toots an interesting section of the Mandelbrot Set.

Unfortunately, when getting to the actual bot part of the project, I found very little in the way of tutorials or examples in Ruby, so I'm writing down what I did while it's still fresh in my mind.

1. Create an Account

First, we need an account on a Mastodon instance. I'm using http://botsin.space, an instance for bot writers. This is pretty straightfoward: just fill in your details in the signup form and away you go.

2. Get Credentials

Getting credentials is an overly complicated process that (fortunately) there are tools to help with. Basically you:

  1. Register your application on the Mastodon instance. This gives you two big numbers (the client ID and client secret).

  2. Using these numbers plus the username and password of your bot's account, log in. This gives you your bot's access token. Hold on to this.

  3. From now on, the bot will use the access token to authenticate itself. You can invalidate the access token at any time from the bot account's settings (Settings -> Authorized Apps).

Unfortunately, the Ruby Mastodon client API library doesn't have an easy way to do this for you. After wrestling with it for a while, I ended up just using Darius Kazemi's registration app at https://tinysubversions.com/notes/mastodon-bot/. It's a nice little webapp that does everything reasonably securely. Just log into your instance and, while logged in, go to the above URL and follow the directions. (You do need to have curl installed and available, though.)

Failing that and if you have a Python installation handy, you can use the Python Mastodon module. Allison Parish's excellent guide for doing that is here.

3. Install the Mastodon API Gem

Assuming you have Ruby installed, it's a simple matter of typing

gem install mastodon-api

Unfortunately, as of this writing, there's a bug in the current release which has been fixed in development but which breaks the bot.

I worked around this by creating a Gemfile and listing a recent git commit as the version. (Did you know that bundle can install gems from GitHub? It can!)

It looks like this:

source 'https://rubygems.org'

gem "mastodon-api", require: "mastodon", github: "tootsuite/mastodon-api",
    branch: "master", ref: "a3ff60a"

Then, you cd to the project directory and run bundle:

bundle

(Or, if you want to install the gems locally and aren't using rvm or rbenv, you can install the gems locally:

bundle install --path ~/gems/

But you knew that, right?)

4. Basic Tooting

We are now ready to write a simple bot. This is done in two steps.

First, we create the client object:

client = Mastodon::REST::Client.new(
  base_url: 'https://botsin.space',
  bearer_token: 'bdc1fe7113d7cb9ea0f5d25')

The bearer_token argument is the token you got in step 2. (The real one will be longer than the one in the example.)

Then, we post the toot:

client.create_status("test status!!!!!")

And that should do it.

Here's the complete script:

require 'rubygems'
require 'bundler/setup'

require 'mastodon'

client = Mastodon::REST::Client.new(
  base_url: 'https://botsin.space',
  bearer_token: 'bdc1fe7113d7cb9ea0f5d25')

client.create_status("test status!!!!!")

One thing: because it's using bundler, the script must either be run from the directory containing the Gemfile or you will need to set the BUNDLE_GEMFILE environment variable to point to it.

I ended up doing the latter by writing a shell script to set things up and then launch the bot:

#!/bin/bash

botdir="$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)"

export BUNDLE_GEMFILE="$botdir/Gemfile"
ruby "$botdir/basic_toot.rb" "$@"

The tricky expression in the second non-empty line extracts the directory containing the shell script itself; it figures out the location of the Gemfile from that.

5. Tooting With Media

Going from here to attaching a picture is pretty simple.

You set up the client as before and then upload the picture with upload_media():

form = HTTP::FormData::File.new("#{FILE}.png")
media = client.upload_media(form)

It's possible that you don't need to wrap your filename in a HTTP::FormData::File object (the API docs seem to imply that) but I wasn't able to get that to work and anyway, this doesn't hurt.

The media object you get back from the call to upload_media() contains various bits of information about it including an ID. This gets attached to the toot:

status = File.open("#{FILE}.txt") {|fh| fh.read}    # Get the toot text
sr = client.create_status(status, nil, [media.id])

It's in an array because (of course) you can attach multiple images to a toot.

Here's the entire script:

require 'rubygems'
require 'bundler/setup'

require 'mastodon'

FILE = "media_dir/toot"

client = Mastodon::REST::Client.new(
  base_url: 'https://botsin.space',
  bearer_token: 'bdc1fe7113d7cb9ea0f5d25')

form = HTTP::FormData::File.new("#{FILE}.png")
media = client.upload_media(form)

status = File.open("#{FILE}.txt") {|fh| fh.read}
sr = client.create_status(status, nil, [media.id])

This is mostly I use, plus the whole image plotting stuff, some logging stuff and a bunch of safety and error detection stuff.

6. Auth Token Management

You will note that I store the auth token as a string literal. I do this to simplify the example code but in general, it's a Bad Idea; mixing your credentials with your source code is just asking for your login details to get published on the open Internet. Keep them far away from each other.

For my bot, I use a YAML file that sits in the bot's work directory (which is not part of the source tree). Other options might be to pass it as a command-line argument or store it in an environment variable.

Other Resources

Update

As I discovered the hard way when the microSD card holding the bot's host's filesystem crapped out, the 2.0.0 version of the API changes a couple of things:

  1. client.create_status now takes most of its arguments as keyword arguments, requiring only the status text. The media ID list is passed as media_ids.

  2. client.upload_media now(?) optionally takes an ordinary file handle.

I ended up doing the equivalent of

media = open("#{FILE}.png", "rb") { |fh| client.upload_media(fh) }
status = File.open("#{FILE}.txt") {|fh| fh.read}
sr = client.create_status(status, media_ids: [media.id])

which worked again.


#   Posted 2017-12-15 03:46:21 UTC; last changed 2020-03-04 03:38:04 UTC



Site Updates

So I changed a couple of things on this website.

Firstly, I disabled Disqus comments from the blog. The service does a lot of cross-site shenanigans that have the potential to be a huge privacy violation. And anyway, nobody's ever used it except for me, the time I tested Disqus to see if it worked.

If you want to comment on a blog post, feel free to send me email and I'll update the post if I think it's relevant.

Secondly, I made a few small changes to the way some pages are laid out. This was part of completely rewriting how the non-blog parts of this site are rendered.

The website is generated from a collection of text files via a Perl script, all of which are stored in a git repository. Previously, that script invoked Infernal Icecube to do the rendering. The problem with this is that ii is pretty much abandoned, so updating the website usually involved first figuring out what dependency got left behind in the last system update. Also, ii wasn't in the repo, so I often had to first find a copy and install it. (It also doesn't help that macOS has a case-insensitive filesystem and the ii repo contains a file named ii and a directory named II in the root.)

I replaced it all with a script that renders using Text::Markdown, which I snagged and checked into the repo along with the rest of the site. Now, the only external dependencies are perl, make and rsync.

This meant that I had to manually convert my content from Infernal Icecube's markup format to Markdown. Fortunately, ii markup is basically an independent reinvention of Markdown, so that was pretty straightforward.

None of this should change how the site looks, but having had to review the entire site meant I saw its shortcomings anew, and so I've fixed a few things here and there. That's all.


#   Posted 2017-09-10 21:23:46 UTC; last changed 2017-09-10 21:25:14 UTC



RRSPs explained for Americans

Canada has a retirement savings vehicle called the Registered Retirement Savings Plan, (i.e. the RRSP). Money that is put into an RRSP is not taxed until you take it out, sometime after retirement. This isn't just pre-tax income: money that you put into an RRSP is actually not considered to exist for tax purposes.

So let's assume that Alice earns $100,000 a year and is taxed at a flat 10%1. Her employer withholds this money and sends it to Revenue Canada:

Earned          = $100,000
Tax             = $100,000/10 = $10,000

But during the year, Alice puts $10000 into an RRSP. When it comes time to compute her actual taxes for that year, the contribution is subtracted from her earned taxes, so as far as Revenue Canada is concerned, her tax status is:

Earned          = $90,000
Tax             = $90,000/10 = $9,000
Witheld         = $10,000
Refund          = $1,000

Because her employer has witheld too much taxes, she gets the rest back.

And this is where things get interesting. Alice, wanting to maximize her retirement savings, decides to also contribute the tax refund. She does this by borrowing2 $1,000 and contributing it before the end of the current year3. So now her taxes look like this:

Earned          = $89,000
Tax             = $89,000/10 = $8,900
Witheld         = $10,000
Refund          = $1,100

The extra $1,000 reduces her taxable income to $89,000 which adds another $100 to the refund. So she borrows another4 $100:

Earned          = $88,900
Tax             = $88,900/10 = $8890
Witheld         = $10,000
Refund          = $1,110

Which adds $10 to the refund. So she borrows another $10 to put in the RRSP:

Earned          = $88,990
Tax             = $88,990/10 = $8899
Witheld         = $10,000
Refund          = $1,111

At which point it's not really worth continuing. But the important thing is that she managed to get another $111 for free5.

And in real life where the tax rate is a lot higher than 10%, you'll also get correspondingly more bonus money, which is why this is a thing.


  1. The real tax rate is higher and more complicated; 10% keeps it simple without detracting from my main point. 

  2. The loan itself costs money, but it's pretty cheap. It lasts a couple of months and has the RRSP as collateral. 

  3. Actually, the deadline's the end of February of the following year. 

  4. More precisely, figures this all out in advance and adds it to the current loan. 

  5. Minus the cost of the loan, of course. 


#   Posted 2017-09-10 20:20:16 UTC; last changed 2017-09-10 21:44:18 UTC