Search Results: "ag"

29 October 2016

Lars Wirzenius: Obnam 1.20 released

I have just released version 1.20 of Obnam, my backup program. It's been nine months since the previous release, and that's a long time: I've had an exciting year, and not entirely in a good way. Unfortuntely that's eaten up a lot of my free time and enthusiasm for my hobby projects. See below for a snippet of NEWS, with a summary of the user-visible changes. A lot of the effort has gone into improving FORMAT GREEN ALBATROSS, but that isn't documented in the NEWS file. I've received patches and actionable bug reports from a number of people, and I'm grateful for those. I try to credit them by name in the NEWS file. Obnam NEWS This file summarizes changes between releases of Obnam. NOTE: Obnam has an EXPERIMENTAL repository format under development, called green-albatross-20160813. It is NOT meant for real use. It is likely to change in incompatible ways without warning. DO NOT USE it unless you're willing to lose your backup. Version 1.20, released 2016-10-29 Minor changes: Bug fixes:

Iain R. Learmonth: live-wrapper 0.4 released!

Last week saw the quiet upload of live-wrapper 0.4 to unstable. I would have blogged at the time, but there is another announcement coming later in this blog post that I wanted to make at the same time. live-wrapper is a wrapper around vmdebootstrap for producing bootable live images using Debian GNU/Linux. Accompanied by the live-tasks package in Debian, this provides the toolchain and configuration necessary for building live images using Cinnamon, GNOME, KDE, LXDE, MATE and XFCE. There is also work ongoing to add a GNUstep image to this. Building a live image with live-wrapper is easy:
sudo apt install live-wrapper
sudo lwr
This will build you a file named output.iso in the current directory containing a minimal live-image. You can the test this in QEMU:
qemu-system-x86_64 -m 2G -cdrom live.iso
You can find the latest documentation for live-wrapper here and any feedback you have is appreciated. So far it looks that booting from CD and USB with both ISOLINUX (BIOS) and GRUB (EFI) are all working as expected on real hardware. The second announcement that I wanted to accompany this announcement is that we will be running a vmdebootstrap sprint where we will be working on live-wrapper at the MiniDebConf in Cambridge. I will be working on installer integration while Ana Custura will be investigating bootloaders and their customisation. I d like to thank the Debian Project and those who have given donations to it for supporting our travel and accomodation costs for this sprint.

Iain R. Learmonth: live-wrapper 0.4 released!

Last week saw the quiet upload of live-wrapper 0.4 to unstable. I would have blogged at the time, but there is another announcement coming later in this blog post that I wanted to make at the same time. live-wrapper is a wrapper around vmdebootstrap for producing bootable live images using Debian GNU/Linux. Accompanied by the live-tasks package in Debian, this provides the toolchain and configuration necessary for building live images using Cinnamon, GNOME, KDE, LXDE, MATE and XFCE. There is also work ongoing to add a GNUstep image to this. Building a live image with live-wrapper is easy:
sudo apt install live-wrapper
sudo lwr
This will build you a file named output.iso in the current directory containing a minimal live-image. You can the test this in QEMU:
qemu-system-x86_64 -m 2G -cdrom live.iso
You can find the latest documentation for live-wrapper here and any feedback you have is appreciated. So far it looks that booting from CD and USB with both ISOLINUX (BIOS) and GRUB (EFI) are all working as expected on real hardware. The second announcement that I wanted to accompany this announcement is that we will be running a vmdebootstrap sprint where we will be working on live-wrapper at the MiniDebConf in Cambridge. I will be working on installer integration while Ana Custura will be investigating bootloaders and their customisation. I d like to thank the Debian Project and those who have given donations to it for supporting our travel and accomodation costs for this sprint.

Jaldhar Vyas: Dawkins Weasel

Happy Dhanteras from Bappy Lahiri
It's already Dhan Terash so I better pick up the pace if I want to beat my blogging challenge before Diwali so in this post I'll discuss a program I wrote earlier this year.
I dread to look up anything on Wikipedia because I always end up going down a rabbit hole and surfacing hours later on a totally unrelated topic. Case in point, some months ago, I ended up on the page of the title. This is an interesting little experiment illustrating how random selection can result in the evolution of a specific form. The algorithm is:

  1. Start with a random string of 28 characters.
  2. Make 100 copies of this string, with a 5% chance per character of that character being replaced with a random character.
  3. Compare each new string with "METHINKS IT IS LIKE A WEASEL", and give each a score (the number of letters in the string that are correct and in the correct position).
  4. If any of the new strings has a perfect score (== 28), halt.
  5. Otherwise, take the highest scoring string, and go to step 2.
I had to try this myself so I wrote a little implementation in C++. A sample run looks like this:
  
$ ./weasel
0000 DNCFICBLUZVC JF KKNVJJASCJRW (0)
0001 DNIFICOLUZVC JFLIKNVAJASCJEW (6)
0002 DNNWICKSUZVCRSFLIKNVA ASCJEL (11)
0003 DNNWICKSUZVCRSFLIKNVA ASCJEL (11)
0004 MNNVICKSQZVCRSFLIKNVA WSCJEL (13)
0005 MENVICKSQZVCRSFLIKNVA WSCJEL (14)
0006 MENVISKS ZTCRSFLIKNVA WLCJEL (16)
0007 MENVISKS ZTCRSFLIKNVA WLCJEL (16)
0008 MEDHISKS ZTCISFLIKNVA WLCJEL (18)
0009 MEDHISKS ZTCISFLIKNVA WLCJEL (18)
0010 MEDHISKS ZTCISFLIKNVA WLCJEL (18)
0011 MEDHISKS ZTCIS LIKTKA WLCZEL (19)
0012 MEDHISKS ZTCIS LIKTKA WLCZEL (19)
0013 MEDHISKS ZTCIS LIKT A WLCZEL (20)
0014 MEDHISKS ZTCIS LIKT A WLCZEL (20)
0015 MEDHISKS ZTCIS LIKE A WLAZEL (22)
0016 MEDHIGKS ITCIS LIKE A WLAZEL (23)
0017 MEDHIGKS ITCIS LIKE A WLAZEL (23)
0018 MEDHIGKS ITCIS LIKE A WLAZEL (23)
0019 MEDHIGKS ITCIS LIKE A WLAZEL (23)
0020 MEDHIGKS ITCIS LIKE A WLAZEL (23)
0021 MEDHIGKS ITCIS LIKE A WLAZEL (23)
0022 METHINKS ITCIS LIKE A WLASEL (26)
0023 METHINKS ITCIS LIKE A WLASEL (26)
0024 METHINKS ITCIS LIKE A WLASEL (26)
0025 METHINKS ITCIS LIKE A WEASEL (27)
0026 METHINKS ITCIS LIKE A WEASEL (27)
0027 METHINKS ITCIS LIKE A WEASEL (27)
0028 METHINKS ITCIS LIKE A WEASEL (27)
0029 METHINKS ITCIS LIKE A WEASEL (27)
0030 METHINKS ITCIS LIKE A WEASEL (27)
0031 METHINKS ITCIS LIKE A WEASEL (27)
0032 METHINKS ITCIS LIKE A WEASEL (27)
0033 METHINKS ITCIS LIKE A WEASEL (27)
0034 METHINKS ITCIS LIKE A WEASEL (27)
0035 METHINKS ITCIS LIKE A WEASEL (27)
0036 METHINKS ITCIS LIKE A WEASEL (27)
0037 METHINKS ITCIS LIKE A WEASEL (27)
0038 METHINKS ITCIS LIKE A WEASEL (27)
0039 METHINKS ITCIS LIKE A WEASEL (27)
0040 METHINKS ITCIS LIKE A WEASEL (27)
0041 METHINKS ITCIS LIKE A WEASEL (27)
0042 METHINKS ITCIS LIKE A WEASEL (27)
0043 METHINKS ITCIS LIKE A WEASEL (27)
0044 METHINKS ITCIS LIKE A WEASEL (27)
0045 METHINKS ITCIS LIKE A WEASEL (27)
0046 METHINKS ITCIS LIKE A WEASEL (27)
0047 METHINKS ITCIS LIKE A WEASEL (27)
0048 METHINKS ITCIS LIKE A WEASEL (27)
0049 METHINKS ITCIS LIKE A WEASEL (27)
0050 METHINKS ITCIS LIKE A WEASEL (27)
0051 METHINKS ITCIS LIKE A WEASEL (27)
0052 METHINKS ITCIS LIKE A WEASEL (27)
0053 METHINKS ITCIS LIKE A WEASEL (27)
0054 METHINKS IT IS LIKE A WEASEL (28)

My program lets you adjust the input string, the number of copies, and the mutation threshold. I also thought it might be interesting to implement the Generator design pattern. In C++ this is done by making a class which implements begin() and end() methods and atleast a forward iterator. You can find the source code on Github.

28 October 2016

Russ Allbery: Term::ANSIColor 4.06

A small maintenance release to my Perl module for generating and manipulating ANSI color escape sequences. In 4.00, I added 256-color support, using ansi0 through ansi15 for the colors that match the normal 16 colors and then rgbNNN and greyN names for the RGB and greyscale colors. One module user requested the ability to address all of the colors via ansi0 through ansi255 so that there's a consistent naming scheme for all the colors. This release adds that. (The more specific names are still returned by uncolor() when reversing escape sequences.) This module tends to get included all over the place, so I did spend a bit of time when preparing this release trying to determine if adding a bunch more entries to various internal hash tables and arrays would noticably increase memory usage, since Perl is notoriously bad about memory consumption. It does cause a small increase, but it's on the order of about 100KB, and a minimum Perl program to load the module requires about 5.5MB of memory (aie), so it wasn't enough for me to do anything about. It does look like if I lazily added entries to the built-in hash tables and instead added some more code to calculate escape sequences on the fly, I could save about 300KB of memory usage in the module. Not sure if it's worth it given how small the memory usage is compared to Perl itself, but maybe I'll look at that later when I'm feeling like fiddling with the module again. (Oh, and all the documentation was regenerated by DocKnot, since I'm still having fun with that. It needed a few new features, which will be in an upcoming 1.01 release.) You can get the latest version from the Term::ANSIColor distribution page.

Matthew Garrett: Of course smart homes are targets for hackers

The Wirecutter, an in-depth comparative review site for various electrical and electronic devices, just published an opinion piece on whether users should be worried about security issues in IoT devices. The summary: avoid devices that don't require passwords (or don't force you to change a default and devices that want you to disable security, follow general network security best practices but otherwise don't worry - criminals aren't likely to target you.

This is terrible, irresponsible advice. It's true that most users aren't likely to be individually targeted by random criminals, but that's a poor threat model. As I've mentioned before, you need to worry about people with an interest in you. Making purchasing decisions based on the assumption that you'll never end up dating someone with enough knowledge to compromise a cheap IoT device (or even meeting an especially creepy one in a bar) is not safe, and giving advice that doesn't take that into account is a huge disservice to many potentially vulnerable users.

Of course, there's also the larger question raised by the last week's problems. Insecure IoT devices still pose a threat to the wider internet, even if the owner's data isn't at risk. I may not be optimistic about the ease of fixing this problem, but that doesn't mean we should just give up. It is important that we improve the security of devices, and many vendors are just bad at that.

So, here's a few things that should be a minimum when considering an IoT device:
  • Does the vendor publish a security contact? (If not, they don't care about security)
  • Does the vendor provide frequent software updates, even for devices that are several years old? (If not, they don't care about security)
  • Has the vendor ever denied a security issue that turned out to be real? (If so, they care more about PR than security)
  • Is the vendor able to provide the source code to any open source components they use? (If not, they don't know which software is in their own product and so don't care about security, and also they're probably infringing my copyright)
  • Do they mark updates as fixing security bugs? (If not, they care more about hiding security issues than fixing them)
  • Has the vendor ever threatened to prosecute a security researcher? (If so, again, they care more about PR than security)
  • Does the vendor provide a public minimum support period for the device? (If not, they don't care about security or their users)

    I've worked with big name vendors who did a brilliant job here. I've also worked with big name vendors who responded with hostility when I pointed out that they were selling a device with arbitrary remote code execution. Going with brand names is probably a good proxy for many of these requirements, but it's insufficient.

    So here's my recommendations to The Wirecutter - talk to a wide range of security experts about the issues that users should be concerned about, and figure out how to test these things yourself. Don't just ask vendors whether they care about security, ask them what their processes and procedures look like. Look at their history. And don't assume that just because nobody's interested in you, everybody else's level of risk is equal.


  • comment count unavailable comments

    Alessio Treglia: The logical contradictions of the Universe

    Ouroboros

    Ouroboros

    Is Erwin Schr dinger s wave function which did in the atomic and subatomic world an operation altogether similar to the one performed by Newton in the macroscopic world an objective reality or just a subjective knowledge? Physicists, philosophers and epistemologist have debated at length on this matter. In 1960, theoretical physicist Eugene Wigner has proposed that the observer s consciousness is the dividing line that triggers the collapse of the wave function[1], and this theory was later taken up and developed in recent years. The rules of quantum mechanics are correct but there is only one system which may be treated with quantum mechanics, namely the entire material world. There exist external observers which cannot be treated within quantum mechanics, namely human (and perhaps animal) minds, which perform measurements on the brain causing wave function collapse [2]. The English mathematical physicist and philosopher of science Roger Penrose developed the hypothesis called Orch-OR (Orchestrated objective reduction) according to which consciousness originates from processes within neurons, rather than from the connections between neurons (the conventional view). The mechanism is believed to be a quantum physical process called objective reduction which is orchestrated by the molecular structures of the microtubules of brain cells (which constitute the cytoskeleton of the cells themselves). Together with the physician Stuart Hameroff, Penrose has suggested a direct relationship between the quantum vibrations of microtubules and the formation of consciousness.

    <Read More [by Fabio Marzocca]>

    Jaldhar Vyas: Get Ready For Bikini Season With These n Weird Tricks

    It all started last June when my son had his Janoi (Yagnopavita) ceremony -- the ritual by which a Brahmana boy becomes "twice-born" and eligible to study the Vedas. As well as a profound religious experience, it is also an important social occasion with a reception for as many friends and family as can attend. (I think our final guest total was ~250.) This meant new outfits for everyone which might be exciting for some people but not me. I still don't know why I couldn't just keep wearing the khes and pitambar from the puja but no, apparently that's a faux pas. So I relented and agreed to wear my "darbari" suit from my wedding. And it didn't fit. I knew I had gained some weight in the intermediate 17 years but the thing was sitcom levels of too small. I ended up having to purchase a new one, a snazzy (and shiny!) maroon number with gold stripes (or were they black stripes?) Problem having been solved, much was eaten, more weight was gained and then I forgot about the whole thing. Tip 1: Actually Do Something. I have over the years tried to improve my physical condition but it has never gotten very far. For instance I have a treadmill/coatrack and a couple of years ago I began using it in earnest. I got to the point where I actually ran a 10K race without dying. But I did not train systematically and I ended up in some pain which caused me to stop working out for a while and then I never got around to restarting. Diets have also failed because I don't have a clear idea of what and how much I am eating. All I know is that women go into the kitchen and when they come out they have food. By what eldritch process this occurs is a mystery, I just eat whats given to me thankful that the magic happens. Once I was moved to try and help but quickly fell afoul of the lack of well-defined algorithms in Gujarati home cooking.
    "How much saffron should I add?"
    "this much."
    "How much is this much in SI units?"
    "You're annoying me. Get out."
    Fast forward to March of this year. For my birthday, my wife got me a Fitbit fitness tracker. This is what I had needed all this time. It measure heart rate, distance travelled, time slept and several other pieces of info you can use to really plan a fitness regimen rationally. For example, I was chagrined to learn that sometimes when I'm at the computer, I am so immobile that the fitbit thought I was asleep. So I started planning to taken more frequent breaks. (A recent firmware upgrade has added the ability to nudge to walk atleast 250 paces each daytime hour which is handy for this.) Also by checking my heart rate I discovered that I went on the treadmill I ran too fast thereby stressing my body for little gain and ending up going too slow to get much aerobic effect. Now I can pace myself appropriately for maximum cardiac efficiency without ending up injuring myself and giving up. I also get a little more activity each day by simple changes such as taking the stairs instead of the lift and instead of getting off at the 14th street PATH I go all the way to 34th street and walk down. Tip 2: You must have data in order to see what you did right or wrong and to plan what you need to do moving forward. One caveat about these fitness trackers. They are not anywhere as accurate as a proper checkup from a doctor who specializes in such things. If you want to do any kind of pro or amateur athletics you probably should not rely on them but for the average shlub who just wants to avoid appearing on the news being winched off his sofa by the fire brigade they are good enough. Another practice I began was keeping a food diary. It can be a real eye-opener to see how much you are actually eating. It is probably much more than you thought. I am fortunate that my diet is pretty good to begin with. Vegetarian, (not vegan, Hindus eat dairy products,) mostly home-cooked with fresh ingredients, not fried or processed, and I don't drink alcohol. However there were a few optimizations I could make. I drink a lot of soda; atleast two cans a day. I really ought to stop altogether but in lieu of that I have atleast switched from Coke to Coke Zero thereby saving a lot of empty calories. I now eat 4 rotlis with my dinner instead of six. We as a family eat more green vegetables instead of potatos, skim milk instead of whole fat, canola oil instead of corn oil, and less rice and don't slather ghee on everything quite so much. One entirely new practice I've adopted that may seem faddish but works for me is intermittent fasting. The idea is to steadily train your body to need less food by eating all your days allowed amount pf calories during a 6-8 hour window and not eating at all during the remaining time. It's hard to get used to for many people but I fast atleast 2-3 times a month for religious reasons anyway so I adapted pretty quickly. The fitbit tells me how many calories I am expending and how many I can eat to maintain a healthy level of weight loss but other than that I don't bother with "food groups" or specific diets such as paleo, or low-carb etc. As long as what you eat is reasonably balanced and you are burning more calories than you are adding, it should be enough for weight loss. Indeed from the end of March to now, I've lost 3 stones (20Kg) even with the occasional "cheat" day. Tip 3: All published diets are bullshit without scientifically proven efficacy. Don't bother with them. Experiment instead and see what works for you and your metabolism. As long as you are getting all the proper nutrients (you shouldn't need a supplement unless you have an actual medical condition.) and you have a net calorie deficit, it's all good. If you eat food you enjoy, you are more likely to stick to your diet. The proper amount of sleep is one area of a healthy lifestyle I am still doing poorly in and the reasons are not all raven-related. I have always had problems with insomnia and was once actually diagnosed with sleep apnea. Losing weight has helped a lot but the fitbit is still reporting that I toss and turn a lot during the night. And that's when I'm in bed in the first place. I stay up much too late which can also lead to subsidiary bad behaviours such as midnight snacking. It's something I need to work on. Tip 4: Stop blogging at all hours of the night, It's not doing you any good. So that's what I'm doing. Moving forward, I need to deal with the sleep thing and I would also like to start some program of strength-training, I'm doing ok in terms of aerobic exercise but from what I've read, you also have to build up muscles to keep weight loss permanent. The difficulty is that it would involve joining a gym and then actually going to that gym so I've put it off for now. The immediate threat is Diwali (and Thanksgiving and Christmas...) My wife bought 4 lbs of sweets today and I can feel their presence in the fridge calling to me.

    27 October 2016

    Russ Allbery: DocKnot 1.00

    I'm a bit of a perfectionist about package documentation, and I'm also a huge fan of consistency. As I've slowly accumulated more open source software packages (alas, fewer new ones these days since I have less day-job time to work on them), I've developed a standard format for package documentation files, particularly the README in the package and the web pages I publish. I've iterated on these, tweaking them and messing with them, trying to incorporate all my accumulated wisdom about what information people need. Unfortunately, this gets very tedious, since I have dozens of packages at this point and rarely want to go through the effort of changing every one of them every time I come up with a better way of phrasing something or change some aspect of my standard package build configuration. I also have the same information duplicated in multiple locations (the README and the web page for the package). And there's a lot of boilerplate that's common for all of my packages that I don't want to keep copying (or changing when I do things like change all eyrie.org URLs to HTTPS). About three years ago, I started seriously brainstorming ways of automating this process. I made a start on it during one self-directed day at my old job at Stanford, but only got it far enough to generate a few basic files. Since then, I keep thinking about it, keep wishing I had it, and keep not putting the work into finishing it. During this vacation, after a week and a half of relaxing and reading, I finally felt like doing a larger development project and finally started working on this for long enough to build up some momentum. Two days later, and this is finally ready for an initial release. DocKnot uses metadata (which I'm putting in docs/metadata) that's mostly JSON plus some documentation fragments and generates README, the web page for the package (in thread, the macro language I use for all my web pages), and (the other thing I've wanted to do and didn't want to tackle without this tool) README.md, a Markdown version of README that will look nice on GitHub. The templates that come with the package are all rather specific to me, particularly the thread template which would be unusable by anyone else. I have no idea if anyone else will want to use this package (and right now the metadata format is entirely undocumented). But since it's a shame to not release things as free software, and since I suspect I may need to upload it to Debian since, technically, this tool is required to "build" the README file distributed with my packages, here it is. I've also uploaded it to CPAN (it's my first experiment with the App::* namespace for things that aren't really meant to be used as a standalone Perl module). You can get the latest version from the DocKnot distribution page (which is indeed generated with DocKnot). Also now generated with DocKnot are the rra-c-util and C TAP Harness distribution pages. Let me know if you see anything weird; there are doubtless still a few bugs.

    26 October 2016

    Steinar H. Gunderson: Why does software development take so long?

    Nageru 1.4.0 is out (and on its way through the Debian upload process right now), so now you can do live video mixing with multichannel audio to your heart's content. I've already blogged about most of the interesting new features, so instead, I'm trying to answer a question: What took so long? To be clear, I'm not saying 1.4.0 took more time than I really anticipated (on the contrary, I pretty much understood the scope from the beginning, and there was a reason why I didn't go for building this stuff into 1.0.0); but if you just look at the changelog from the outside, it's not immediately obvious why multichannel audio support should take the better part of three months of develoment. What I'm going to say is of course going to be obvious to most software developers, but not everyone is one, and perhaps my experiences will be illuminating. Let's first look at some obvious things that isn't the case: First of all, development is not primarily limited by typing speed. There are about 9,000 lines of new code in 1.4.0 (depending a bit on how you count), and if it was just about typing them in, I would be done in a day or two. On a good keyboard, I can type plain text at more than 800 characters per minute but you hardly ever write code for even a single minute at that speed. Just as when writing a novel, most time is spent thinking, not typing. I also didn't spend a lot of time backtracking; most code I wrote actually ended up in the finished product as opposed to being thrown away. (I'm not as lucky in all of my projects.) It's pretty common to do so if you're in an exploratory phase, but in this case, I had a pretty good idea of what I wanted to do right from the start, and that plan seemed to work. This wasn't a difficult project per se; it just needed to be done (which, in a sense, just increases the mystery). However, even if this isn't at the forefront of science in any way (most code in the world is pretty pedestrian, after all), there's still a lot of decisions to make, on several levels of abstraction. And a lot of those decisions depend on information gathering beforehand. Let's take a look at an example from late in the development cycle, namely support for using MIDI controllers instead of the mouse to control the various widgets. I've kept a pretty meticulous TODO list; it's just a text file on my laptop, but it serves the purpose of a ghetto bugtracker. For 1.4.0, it contains 83 work items (a single-digit number is not ticked off, mostly because I decided not to do those things), which corresponds roughly 1:2 to the number of commits. So let's have a look at what the ~20 MIDI controller items went into. First of all, to allow MIDI controllers to influence the UI, we need a way of getting to it. Since Nageru is single-platform on Linux, ALSA is the obvious choice (if not, I'd probably have to look for a library to put in-between), but seemingly, ALSA has two interfaces (raw MIDI and sequencer). Which one do you want? It sounds like raw MIDI is what we want, but actually, it's the sequencer interface (it does more of the MIDI parsing for you, and generally is friendlier). The first question is where to start picking events from. I went the simplest path and just said I wanted all events anything else would necessitate a UI, a command-line flag, figuring out if we wanted to distinguish between different devices with the same name (and not all devices potentially even have names), and so on. But how do you enumerate devices? (Relatively simple, thankfully.) What do you do if the user inserts a new one while Nageru is running? (Turns out there's a special device you can subscribe to that will tell you about new devices.) What if you get an error on subscription? (Just print a warning and ignore it; it's legitimate not to have access to all devices on the system. By the way, for PCM devices, all of these answers are different.) So now we have a sequencer device, how do we get events from it? Can we do it in the main loop? Turns out it probably doesn't integrate too well with Qt, but it's easy enough to put it in a thread. The class dealing with the MIDI handling now needs locking; what mutex granularity do we want? (Experience will tell you that you nearly always just want one mutex. Two mutexes give you all sorts of headaches with ordering them, and nearly never gives any gain.) ALSA expects us to poll() a given set of descriptors for data, but on shutdown, how do you break out of that poll to tell the thread to go away? (The simplest way on Linux is using an eventfd.) There's a quirk where if you get two or more MIDI messages right after each other and only read one, poll() won't trigger to alert you there are more left. Did you know that? (I didn't. I also can't find it documented. Perhaps it's a bug?) It took me some looking into sample code to find it. Oh, and ALSA uses POSIX error codes to signal errors (like nothing more is available ), but it doesn't use errno. OK, so you have events (like controller 3 was set to value 47 ); what do you do about them? The meaning of the controller numbers is different from device to device, and there's no open format for describing them. So I had to make a format describing the mapping; I used protobuf (I have lots of experience with it) to make a simple text-based format, but it's obviously a nightmare to set up 50+ controllers by hand in a text file, so I had to make an UI for this. My initial thought was making a grid of spinners (similar to how the input mapping dialog already worked), but then I realized that there isn't an easy way to make headlines in Qt's grid. (You can substitute a label widget for a single cell, but not for an entire row. Who knew?) So after some searching, I found out that it would be better to have a tree view (Qt Creator does this), and then you can treat that more-or-less as a table for the rows that should be editable. Of course, guessing controller numbers is impossible even in an editor, so I wanted it to respond to MIDI events. This means the editor needs to take over the role as MIDI receiver from the main UI. How you do that in a thread-safe way? (Reuse the existing mutex; you don't generally want to use atomics for complicated things.) Thinking about it, shouldn't the MIDI mapper just support multiple receivers at a time? (Doubtful; you don't want your random controller fiddling during setup to actually influence the audio on a running stream. And would you use the old or the new mapping?) And do you really need to set up every single controller for each bus, given that the mapping is pretty much guaranteed to be similar for them? Making a guess bus button doesn't seem too difficult, where if you have one correctly set up controller on the bus, it can guess from a neighboring bus (assuming a static offset). But what if there's conflicting information? OK; then you should disable the button. So now the enable/disable status of that button depends on which cell in your grid has the focus; how do you get at those events? (Install an event filter, or subclass the spinner.) And so on, and so on, and so on. You could argue that most of these questions go away with experience; if you're an expert in a given API, you can answer most of these questions in a minute or two even if you haven't heard the exact question before. But you can't expect even experienced developers to be an expert in all possible libraries; if you know everything there is to know about Qt, ALSA, x264, ffmpeg, OpenGL, VA-API, libusb, microhttpd and Lua (in addition to C++11, of course), I'm sure you'd be a great fit for Nageru, but I'd wager that pretty few developers fit that bill. I've written C++ for almost 20 years now (almost ten of them professionally), and that experience certainly helps boosting productivity, but I can't say I expect a 10x reduction in my own development time at any point. You could also argue, of course, that spending so much time on the editor is wasted, since most users will only ever see it once. But here's the point; it's not actually a lot of time. The only reason why it seems like so much is that I bothered to write two paragraphs about it; it's not a particular pain point, it just adds to the total. Also, the first impression matters a lot if the user can't get the editor to work, they also can't get the MIDI controller to work, and is likely to just go do something else. A common misconception is that just switching languages or using libraries will help you a lot. (Witness the never-ending stream of software that advertises written in Foo or uses Bar as if it were a feature.) For the former, note that nothing I've said so far is specific to my choice of language (C++), and I've certainly avoided a bunch of battles by making that specific choice over, say, Python. For the latter, note that most of these problems are actually related to library use libraries are great, and they solve a bunch of problems I'm really glad I didn't have to worry about (how should each button look?), but they still give their own interaction problems. And even when you're a master of your chosen programming environment, things still take time, because you have all those decisions to make on top of your libraries. Of course, there are cases where libraries really solve your entire problem and your code gets reduced to 100 trivial lines, but that's really only when you're solving a problem that's been solved a million times before. Congrats on making that blog in Rails; I'm sure you're advancing the world. (To make things worse, usually this breaks down when you want to stray ever so slightly from what was intended by the library or framework author. What seems like a perfect match can suddenly become a development trap where you spend more of your time trying to become an expert in working around the given library than actually doing any development.) The entire thing reminds me of the famous essay No Silver Bullet by Fred Brooks, but perhaps even more so, this quote from John Carmack's .plan has struck with me (incidentally about mobile game development in 2006, but the basic story still rings true):
    To some degree this is already the case on high end BREW phones today. I have a pretty clear idea what a maxed out software renderer would look like for that class of phones, and it wouldn't be the PlayStation-esq 3D graphics that seems to be the standard direction. When I was doing the graphics engine upgrades for BREW, I started along those lines, but after putting in a couple days at it I realized that I just couldn't afford to spend the time to finish the work. "A clear vision" doesn't mean I can necessarily implement it in a very small integral number of days.
    In a sense, programming is all about what your program should do in the first place. The how question is just the what , moved down the chain of abstractions until it ends up where a computer can understand it, and at that point, the three words multichannel audio support have become those 9,000 lines that describe in perfect detail what's going on.

    Daniel Pocock: FOSDEM 2017 Real-Time Communications Call for Participation

    FOSDEM is one of the world's premier meetings of free software developers, with over five thousand people attending each year. FOSDEM 2017 takes place 4-5 February 2017 in Brussels, Belgium. This email contains information about:
    • Real-Time communications dev-room and lounge,
    • speaking opportunities,
    • volunteering in the dev-room and lounge,
    • related events around FOSDEM, including the XMPP summit,
    • social events (the legendary FOSDEM Beer Night and Saturday night dinners provide endless networking opportunities),
    • the Planet aggregation sites for RTC blogs
    Call for participation - Real Time Communications (RTC) The Real-Time dev-room and Real-Time lounge is about all things involving real-time communication, including: XMPP, SIP, WebRTC, telephony, mobile VoIP, codecs, peer-to-peer, privacy and encryption. The dev-room is a successor to the previous XMPP and telephony dev-rooms. We are looking for speakers for the dev-room and volunteers and participants for the tables in the Real-Time lounge. The dev-room is only on Saturday, 4 February 2017. The lounge will be present for both days. To discuss the dev-room and lounge, please join the FSFE-sponsored Free RTC mailing list. To be kept aware of major developments in Free RTC, without being on the discussion list, please join the Free-RTC Announce list. Speaking opportunities Note: if you used FOSDEM Pentabarf before, please use the same account/username Real-Time Communications dev-room: deadline 23:59 UTC on 17 November. Please use the Pentabarf system to submit a talk proposal for the dev-room. On the "General" tab, please look for the "Track" option and choose "Real-Time devroom". Link to talk submission. Other dev-rooms and lightning talks: some speakers may find their topic is in the scope of more than one dev-room. It is encouraged to apply to more than one dev-room and also consider proposing a lightning talk, but please be kind enough to tell us if you do this by filling out the notes in the form. You can find the full list of dev-rooms on this page and apply for a lightning talk at https://fosdem.org/submit Main track: the deadline for main track presentations is 23:59 UTC 31 October. Leading developers in the Real-Time Communications field are encouraged to consider submitting a presentation to the main track. First-time speaking? FOSDEM dev-rooms are a welcoming environment for people who have never given a talk before. Please feel free to contact the dev-room administrators personally if you would like to ask any questions about it. Submission guidelines The Pentabarf system will ask for many of the essential details. Please remember to re-use your account from previous years if you have one. In the "Submission notes", please tell us about:
    • the purpose of your talk
    • any other talk applications (dev-rooms, lightning talks, main track)
    • availability constraints and special needs
    You can use HTML and links in your bio, abstract and description. If you maintain a blog, please consider providing us with the URL of a feed with posts tagged for your RTC-related work. We will be looking for relevance to the conference and dev-room themes, presentations aimed at developers of free and open source software about RTC-related topics. Please feel free to suggest a duration between 20 minutes and 55 minutes but note that the final decision on talk durations will be made by the dev-room administrators. As the two previous dev-rooms have been combined into one, we may decide to give shorter slots than in previous years so that more speakers can participate. Please note FOSDEM aims to record and live-stream all talks. The CC-BY license is used. Volunteers needed To make the dev-room and lounge run successfully, we are looking for volunteers:
    • FOSDEM provides video recording equipment and live streaming, volunteers are needed to assist in this
    • organizing one or more restaurant bookings (dependending upon number of participants) for the evening of Saturday, 4 February
    • participation in the Real-Time lounge
    • helping attract sponsorship funds for the dev-room to pay for the Saturday night dinner and any other expenses
    • circulating this Call for Participation (text version) to other mailing lists
    See the mailing list discussion for more details about volunteering. Related events - XMPP and RTC summits The XMPP Standards Foundation (XSF) has traditionally held a summit in the days before FOSDEM. There is discussion about a similar summit taking place on 2 and 3 February 2017. XMPP Summit web site - please join the mailing list for details. We are also considering a more general RTC or telephony summit, potentially in collaboration with the XMPP summit. Please join the Free-RTC mailing list and send an email if you would be interested in participating, sponsoring or hosting such an event. Social events and dinners The traditional FOSDEM beer night occurs on Friday, 3 February. On Saturday night, there are usually dinners associated with each of the dev-rooms. Most restaurants in Brussels are not so large so these dinners have space constraints and reservations are essential. Please subscribe to the Free-RTC mailing list for further details about the Saturday night dinner options and how you can register for a seat. Spread the word and discuss If you know of any mailing lists where this CfP would be relevant, please forward this email (text version). If this dev-room excites you, please blog or microblog about it, especially if you are submitting a talk. If you regularly blog about RTC topics, please send details about your blog to the planet site administrators:
    Planet site Admin contact
    All projects Free-RTC Planet (http://planet.freertc.org) contact planet@freertc.org
    XMPP Planet Jabber (http://planet.jabber.org) contact ralphm@ik.nu
    SIP Planet SIP (http://planet.sip5060.net) contact planet@sip5060.net
    SIP (Espa ol) Planet SIP-es (http://planet.sip5060.net/es/) contact planet@sip5060.net
    Please also link to the Planet sites from your own blog or web site as this helps everybody in the free real-time communications community. Contact For any private queries, contact us directly using the address fosdem-rtc-admin@freertc.org and for any other queries please ask on the Free-RTC mailing list. The dev-room administration team:

    Joachim Breitner: Showcasing Applicative

    My plan for this week s lecture of the CIS 194 Haskell course at the University of Pennsylvania is to dwell a bit on the concept of Functor, Applicative and Monad, and to highlight the value of the Applicative abstraction. I quite like the example that I came up with, so I want to share it here. In the interest of long-term archival and stand-alone presentation, I include all the material in this post.1

    Imports In case you want to follow along, start with these imports:
    import Data.Char
    import Data.Maybe
    import Data.List
    import System.Environment
    import System.IO
    import System.Exit

    The parser The starting point for this exercise is a fairly standard parser-combinator monad, which happens to be the result of the student s homework from last week:
    newtype Parser a = P (String -> Maybe (a, String))
    runParser :: Parser t -> String -> Maybe (t, String)
    runParser (P p) = p
    parse :: Parser a -> String -> Maybe a
    parse p input = case runParser p input of
        Just (result, "") -> Just result
        _ -> Nothing -- handles both no result and leftover input
    noParserP :: Parser a
    noParserP = P (\_ -> Nothing)
    pureParserP :: a -> Parser a
    pureParserP x = P (\input -> Just (x,input))
    instance Functor Parser where
        fmap f p = P $ \input -> do
    	(x, rest) <- runParser p input
    	return (f x, rest)
    instance Applicative Parser where
        pure = pureParserP
        p1 <*> p2 = P $ \input -> do
            (f, rest1) <- runParser p1 input
            (x, rest2) <- runParser p2 rest1
            return (f x, rest2)
    instance Monad Parser where
        return = pure
        p1 >>= k = P $ \input -> do
            (x, rest1) <- runParser p1 input
            runParser (k x) rest1
    anyCharP :: Parser Char
    anyCharP = P $ \input -> case input of
        (c:rest) -> Just (c, rest)
        []       -> Nothing
    charP :: Char -> Parser ()
    charP c = do
        c' <- anyCharP
        if c == c' then return ()
                   else noParserP
    anyCharButP :: Char -> Parser Char
    anyCharButP c = do
        c' <- anyCharP
        if c /= c' then return c'
                   else noParserP
    letterOrDigitP :: Parser Char
    letterOrDigitP = do
        c <- anyCharP
        if isAlphaNum c then return c else noParserP
    orElseP :: Parser a -> Parser a -> Parser a
    orElseP p1 p2 = P $ \input -> case runParser p1 input of
        Just r -> Just r
        Nothing -> runParser p2 input
    manyP :: Parser a -> Parser [a]
    manyP p = (pure (:) <*> p <*> manyP p)  orElseP  pure []
    many1P :: Parser a -> Parser [a]
    many1P p = pure (:) <*> p <*> manyP p
    sepByP :: Parser a -> Parser () -> Parser [a]
    sepByP p1 p2 = (pure (:) <*> p1 <*> (manyP (p2 *> p1)))  orElseP  pure []
    A parser using this library for, for example, CSV files could take this form:
    parseCSVP :: Parser [[String]]
    parseCSVP = manyP parseLine
      where
        parseLine = parseCell  sepByP  charP ',' <* charP '\n'
        parseCell = do
            charP '"'
            content <- manyP (anyCharButP '"')
            charP '"'
            return content

    We want EBNF Often when we write a parser for a file format, we might also want to have a formal specification of the format. A common form for such a specification is EBNF. This might look as follows, for a CSV file:
    cell = '"',  not-quote , '"';
    line = (cell,  ',', cell    ''), newline;
    csv  =  line ;
    It is straightforward to create a Haskell data type to represent an EBNF syntax description. Here is a simple EBNF library (data type and pretty-printer) for your convenience:
    data RHS
      = Terminal String
        NonTerminal String
        Choice RHS RHS
        Sequence RHS RHS
        Optional RHS
        Repetition RHS
      deriving (Show, Eq)
    ppRHS :: RHS -> String
    ppRHS = go 0
      where
        go _ (Terminal s)     = surround "'" "'" $ concatMap quote s
        go _ (NonTerminal s)  = s
        go a (Choice x1 x2)   = p a 1 $ go 1 x1 ++ "   " ++ go 1 x2
        go a (Sequence x1 x2) = p a 2 $ go 2 x1 ++ ", "  ++ go 2 x2
        go _ (Optional x)     = surround "[" "]" $ go 0 x
        go _ (Repetition x)   = surround " " " " $ go 0 x
        surround c1 c2 x = c1 ++ x ++ c2
        p a n   a > n     = surround "(" ")"
                otherwise = id
        quote '\'' = "\\'"
        quote '\\' = "\\\\"
        quote c    = [c]
    type Production = (String, RHS)
    type BNF = [Production]
    ppBNF :: BNF -> String
    ppBNF = unlines . map (\(i,rhs) -> i ++ " = " ++ ppRHS rhs ++ ";")

    Code to produce EBNF We had a good time writing combinators that create complex parsers from primitive pieces. Let us do the same for EBNF grammars. We could simply work on the RHS type directly, but we can do something more nifty: We create a data type that keeps track, via a phantom type parameter, of what Haskell type the given EBNF syntax is the specification:
    newtype Grammar a = G RHS
    ppGrammar :: Grammar a -> String
    ppGrammar (G rhs) = ppRHS rhs
    So a value of type Grammar t is a description of the textual representation of the Haskell type t. Here is one simple example:
    anyCharG :: Grammar Char
    anyCharG = G (NonTerminal "char")
    Here is another one. This one does not describe any interesting Haskell type, but is useful when spelling out the special characters in the syntax described by the grammar:
    charG :: Char -> Grammar ()
    charG c = G (Terminal [c])
    A combinator that creates new grammar from two existing grammars:
    orElseG :: Grammar a -> Grammar a -> Grammar a
    orElseG (G rhs1) (G rhs2) = G (Choice rhs1 rhs2)
    We want the convenience of our well-known type classes in order to combine these values some more:
    instance Functor Grammar where
        fmap _ (G rhs) = G rhs
    instance Applicative Grammar where
        pure x = G (Terminal "")
        (G rhs1) <*> (G rhs2) = G (Sequence rhs1 rhs2)
    Note how the Functor instance does not actually use the function. How should it? There are no values inside a Grammar! We cannot define a Monad instance for Grammar: We would start with (G rhs1) >>= k = , but there is simply no way of getting a value of type a that we can feed to k. So we will do without a Monad instance. This is interesting, and we will come back to that later. Like with the parser, we can now begin to build on the primitive example to build more complicated combinators:
    manyG :: Grammar a -> Grammar [a]
    manyG p = (pure (:) <*> p <*> manyG p)  orElseG  pure []
    many1G :: Grammar a -> Grammar [a]
    many1G p = pure (:) <*> p <*> manyG p
    sepByG :: Grammar a -> Grammar () -> Grammar [a]
    sepByG p1 p2 = ((:) <$> p1 <*> (manyG (p2 *> p1)))  orElseG  pure []
    Let us run a small example:
    dottedWordsG :: Grammar [String]
    dottedWordsG = many1G (manyG anyCharG <* charG '.')
    *Main> putStrLn $ ppGrammar dottedWordsG
    '', ('', char, ('', char, ('', char, ('', char, ('', char, ('',  
    Oh my, that is not good. Looks like the recursion in manyG does not work well, so we need to avoid that. But anyways we want to be explicit in the EBNF grammars about where something can be repeated, so let us just make many a primitive:
    manyG :: Grammar a -> Grammar [a]
    manyG (G rhs) = G (Repetition rhs)
    With this definition, we already get a simple grammar for dottedWordsG:
    *Main> putStrLn $ ppGrammar dottedWordsG
    '',  char , '.',  char , '.' 
    This already looks like a proper EBNF grammar. One thing that is not nice about it is that there is an empty string ('') in a sequence ( , ). We do not want that. Why is it there in the first place? Because our Applicative instance is not lawful! Remember that pure id <*> g == g should hold. One way to achieve that is to improve the Applicative instance to optimize this case away:
    instance Applicative Grammar where
        pure x = G (Terminal "")
        G (Terminal "") <*> G rhs2 = G rhs2
        G rhs1 <*> G (Terminal "") = G rhs1
        (G rhs1) <*> (G rhs2) = G (Sequence rhs1 rhs2)
    	 
    Now we get what we want:
    *Main> putStrLn $ ppGrammar dottedWordsG
     char , '.',  char , '.' 
    Remember our parser for CSV files above? Let me repeat it here, this time using only Applicative combinators, i.e. avoiding (>>=), (>>), return and do-notation:
    parseCSVP :: Parser [[String]]
    parseCSVP = manyP parseLine
      where
        parseLine = parseCell  sepByP  charG ',' <* charP '\n'
        parseCell = charP '"' *> manyP (anyCharButP '"') <* charP '"'
    And now we try to rewrite the code to produce Grammar instead of Parser. This is straightforward with the exception of anyCharButP. The parser code for that inherently monadic, and we just do not have a monad instance. So we work around the issue by making that a primitive grammar, i.e. introducing a non-terminal in the EBNF without a production rule pretty much like we did for anyCharG:
    primitiveG :: String -> Grammar a
    primitiveG s = G (NonTerminal s)
    parseCSVG :: Grammar [[String]]
    parseCSVG = manyG parseLine
      where
        parseLine = parseCell  sepByG  charG ',' <* charG '\n'
        parseCell = charG '"' *> manyG (primitiveG "not-quote") <* charG '"'
    Of course the names parse are not quite right any more, but let us just leave that for now. Here is the result:
    *Main> putStrLn $ ppGrammar parseCSVG
     ('"',  not-quote , '"',  ',', '"',  not-quote , '"'    ''), '
    ' 
    The line break is weird. We do not really want newlines in the grammar. So let us make that primitive as well, and replace charG '\n' with newlineG:
    newlineG :: Grammar ()
    newlineG = primitiveG "newline"
    Now we get
    *Main> putStrLn $ ppGrammar parseCSVG
     ('"',  not-quote , '"',  ',', '"',  not-quote , '"'    ''), newline 
    which is nice and correct, but still not quite the easily readable EBNF that we saw further up.

    Code to produce EBNF, with productions We currently let our grammars produce only the right-hand side of one EBNF production, but really, we want to produce a RHS that may refer to other productions. So let us change the type accordingly:
    newtype Grammar a = G (BNF, RHS)
    runGrammer :: String -> Grammar a -> BNF
    runGrammer main (G (prods, rhs)) = prods ++ [(main, rhs)]
    ppGrammar :: String -> Grammar a -> String
    ppGrammar main g = ppBNF $ runGrammer main g
    Now we have to adjust all our primitive combinators (but not the derived ones!):
    charG :: Char -> Grammar ()
    charG c = G ([], Terminal [c])
    anyCharG :: Grammar Char
    anyCharG = G ([], NonTerminal "char")
    manyG :: Grammar a -> Grammar [a]
    manyG (G (prods, rhs)) = G (prods, Repetition rhs)
    mergeProds :: [Production] -> [Production] -> [Production]
    mergeProds prods1 prods2 = nub $ prods1 ++ prods2
    orElseG :: Grammar a -> Grammar a -> Grammar a
    orElseG (G (prods1, rhs1)) (G (prods2, rhs2))
        = G (mergeProds prods1 prods2, Choice rhs1 rhs2)
    instance Functor Grammar where
        fmap _ (G bnf) = G bnf
    instance Applicative Grammar where
        pure x = G ([], Terminal "")
        G (prods1, Terminal "") <*> G (prods2, rhs2)
            = G (mergeProds prods1 prods2, rhs2)
        G (prods1, rhs1) <*> G (prods2, Terminal "")
            = G (mergeProds prods1 prods2, rhs1)
        G (prods1, rhs1) <*> G (prods2, rhs2)
            = G (mergeProds prods1 prods2, Sequence rhs1 rhs2)
    primitiveG :: String -> Grammar a
    primitiveG s = G (NonTerminal s)
    The use of nub when combining productions removes duplicates that might be used in different parts of the grammar. Not efficient, but good enough for now. Did we gain anything? Not yet:
    *Main> putStr $ ppGrammar "csv" (parseCSVG)
    csv =  ('"',  not-quote , '"',  ',', '"',  not-quote , '"'    ''), newline ;
    But we can now introduce a function that lets us tell the system where to give names to a piece of grammar:
    nonTerminal :: String -> Grammar a -> Grammar a
    nonTerminal name (G (prods, rhs))
      = G (prods ++ [(name, rhs)], NonTerminal name)
    Ample use of this in parseCSVG yields the desired result:
    parseCSVG :: Grammar [[String]]
    parseCSVG = manyG parseLine
      where
        parseLine = nonTerminal "line" $
            parseCell  sepByG  charG ',' <* newline
        parseCell = nonTerminal "cell" $
            charG '"' *> manyG (primitiveG "not-quote") <* charG '"
    *Main> putStr $ ppGrammar "csv" (parseCSVG)
    cell = '"',  not-quote , '"';
    line = (cell,  ',', cell    ''), newline;
    csv =  line ;
    This is great!

    Unifying parsing and grammar-generating Note how simliar parseCSVG and parseCSVP are! Would it not be great if we could implement that functionality only once, and get both a parser and a grammar description out of it? This way, the two would never be out of sync! And surely this must be possible. The tool to reach for is of course to define a type class that abstracts over the parts where Parser and Grammer differ. So we have to identify all functions that are primitive in one of the two worlds, and turn them into type class methods. This includes char and orElse. It includes many, too: Although manyP is not primitive, manyG is. It also includes nonTerminal, which does not exist in the world of parsers (yet), but we need it for the grammars. The primitiveG function is tricky. We use it in grammars when the code that we might use while parsing is not expressible as a grammar. So the solution is to let it take two arguments: A String, when used as a descriptive non-terminal in a grammar, and a Parser a, used in the parsing code. Finally, the type classes that we except, Applicative (and thus Functor), are added as constraints on our type class:
    class Applicative f => Descr f where
        char :: Char -> f ()
        many :: f a -> f [a]
        orElse :: f a -> f a -> f a
        primitive :: String -> Parser a -> f a
        nonTerminal :: String -> f a -> f a
    The instances are easily written:
    instance Descr Parser where
        char = charP
        many = manyP
        orElse = orElseP
        primitive _ p = p
        nonTerminal _ p = p
    instance Descr Grammar where
        char = charG
        many = manyG
        orElse = orElseG
        primitive s _ = primitiveG s
        nonTerminal s g = nonTerminal s g
    And we can now take the derived definitions, of which so far we had two copies, and define them once and for all:
    many1 :: Descr f => f a -> f [a]
    many1 p = pure (:) <*> p <*> many p
    anyChar :: Descr f => f Char
    anyChar = primitive "char" anyCharP
    dottedWords :: Descr f => f [String]
    dottedWords = many1 (many anyChar <* char '.')
    sepBy :: Descr f => f a -> f () -> f [a]
    sepBy p1 p2 = ((:) <$> p1 <*> (many (p2 *> p1)))  orElse  pure []
    newline :: Descr f => f ()
    newline = primitive "newline" (charP '\n')
    And thus we now have our CSV parser/grammar generator:
    parseCSV :: Descr f => f [[String]]
    parseCSV = many parseLine
      where
        parseLine = nonTerminal "line" $
            parseCell  sepBy  char ',' <* newline
        parseCell = nonTerminal "cell" $
            char '"' *> many (primitive "not-quote" (anyCharButP '"')) <* char '"'
    We can now use this definition both to parse and to generate grammars:
    *Main> putStr $ ppGrammar2 "csv" (parseCSV)
    cell = '"',  not-quote , '"';
    line = (cell,  ',', cell    ''), newline;
    csv =  line ;
    *Main> parse parseCSV "\"ab\",\"cd\"\n\"\",\"de\"\n\n"
    Just [["ab","cd"],["","de"],[]]

    The INI file parser and grammar As a final exercise, let us transform the INI file parser into a combined thing. Here is the parser (another artifact of last week s homework) again using applicative style2:
    parseINIP :: Parser INIFile
    parseINIP = many1P parseSection
      where
        parseSection =
            (,) <$  charP '['
                <*> parseIdent
                <*  charP ']'
                <*  charP '\n'
                <*> (catMaybes <$> manyP parseLine)
        parseIdent = many1P letterOrDigitP
        parseLine = parseDecl  orElseP  parseComment  orElseP  parseEmpty
        parseDecl = Just <$> (
            (,) <*> parseIdent
                <*  manyP (charP ' ')
                <*  charP '='
                <*  manyP (charP ' ')
                <*> many1P (anyCharButP '\n')
                <*  charP '\n')
        parseComment =
            Nothing <$ charP '#'
                    <* many1P (anyCharButP '\n')
                    <* charP '\n'
        parseEmpty = Nothing <$ charP '\n'
    Transforming that to a generic description is quite straightforward. We use primitive again to wrap letterOrDigitP:
    descrINI :: Descr f => f INIFile
    descrINI = many1 parseSection
      where
        parseSection =
            (,) <*  char '['
                <*> parseIdent
                <*  char ']'
                <*  newline
                <*> (catMaybes <$> many parseLine)
        parseIdent = many1 (primitive "alphanum" letterOrDigitP)
        parseLine = parseDecl  orElse  parseComment  orElse  parseEmpty
        parseDecl = Just <$> (
            (,) <*> parseIdent
                <*  many (char ' ')
                <*  char '='
                <*  many (char ' ')
                <*> many1 (primitive "non-newline" (anyCharButP '\n'))
    	    <*  newline)
        parseComment =
            Nothing <$ char '#'
                    <* many1 (primitive "non-newline" (anyCharButP '\n'))
    		<* newline
        parseEmpty = Nothing <$ newline
    This yields this not very helpful grammar (abbreviated here):
    *Main> putStr $ ppGrammar2 "ini" descrINI
    ini = '[', alphanum,  alphanum , ']', newline,  alphanum,  alphanum ,  ' ' 
    But with a few uses of nonTerminal, we get something really nice:
    descrINI :: Descr f => f INIFile
    descrINI = many1 parseSection
      where
        parseSection = nonTerminal "section" $
            (,) <$  char '['
                <*> parseIdent
                <*  char ']'
                <*  newline
                <*> (catMaybes <$> many parseLine)
        parseIdent = nonTerminal "identifier" $
            many1 (primitive "alphanum" letterOrDigitP)
        parseLine = nonTerminal "line" $
            parseDecl  orElse  parseComment  orElse  parseEmpty
        parseDecl = nonTerminal "declaration" $ Just <$> (
            (,) <$> parseIdent
                <*  spaces
                <*  char '='
                <*  spaces
                <*> remainder)
        parseComment = nonTerminal "comment" $
            Nothing <$ char '#' <* remainder
        remainder = nonTerminal "line-remainder" $
            many1 (primitive "non-newline" (anyCharButP '\n')) <* newline
        parseEmpty = Nothing <$ newline
        spaces = nonTerminal "spaces" $ many (char ' ')
    *Main> putStr $ ppGrammar "ini" descrINI
    identifier = alphanum,  alphanum ;
    spaces =  ' ' ;
    line-remainder = non-newline,  non-newline , newline;
    declaration = identifier, spaces, '=', spaces, line-remainder;
    comment = '#', line-remainder;
    line = declaration   comment   newline;
    section = '[', identifier, ']', newline,  line ;
    ini = section,  section ;

    Recursion (variant 1) What if we want to write a parser/grammar-generator that is able to generate the following grammar, which describes terms that are additions and multiplications of natural numbers:
    const = digit,  digit ;
    spaces =  ' '   newline ;
    atom = const   '(', spaces, expr, spaces, ')', spaces;
    mult = atom,  spaces, '*', spaces, atom , spaces;
    plus = mult,  spaces, '+', spaces, mult , spaces;
    expr = plus;
    The production of expr is recursive (via plus, mult, atom). We have seen above that simply defining a Grammar a recursively does not go well. One solution is to add a new combinator for explicit recursion, which replaces nonTerminal in the method:
    class Applicative f => Descr f where
         
        recNonTerminal :: String -> (f a -> f a) -> f a
    instance Descr Parser where
         
        recNonTerminal _ p = let r = p r in r
    instance Descr Grammar where
         
        recNonTerminal = recNonTerminalG
    recNonTerminalG :: String -> (Grammar a -> Grammar a) -> Grammar a
    recNonTerminalG name f =
        let G (prods, rhs) = f (G ([], NonTerminal name))
        in G (prods ++ [(name, rhs)], NonTerminal name)
    nonTerminal :: Descr f => String -> f a -> f a
    nonTerminal name p = recNonTerminal name (const p)
    runGrammer :: String -> Grammar a -> BNF
    runGrammer main (G (prods, NonTerminal nt))   main == nt = prods
    runGrammer main (G (prods, rhs)) = prods ++ [(main, rhs)]
    The change in runGrammer avoids adding a pointless expr = expr production to the output. This lets us define a parser/grammar-generator for the arithmetic expressions given above:
    data Expr = Plus Expr Expr   Mult Expr Expr   Const Integer
        deriving Show
    mkPlus :: Expr -> [Expr] -> Expr
    mkPlus = foldl Plus
    mkMult :: Expr -> [Expr] -> Expr
    mkMult = foldl Mult
    parseExpr :: Descr f => f Expr
    parseExpr = recNonTerminal "expr" $ \ exp ->
        ePlus exp
    ePlus :: Descr f => f Expr -> f Expr
    ePlus exp = nonTerminal "plus" $
        mkPlus <$> eMult exp
               <*> many (spaces *> char '+' *> spaces *> eMult exp)
               <*  spaces
    eMult :: Descr f => f Expr -> f Expr
    eMult exp = nonTerminal "mult" $
        mkPlus <$> eAtom exp
               <*> many (spaces *> char '*' *> spaces *> eAtom exp)
               <*  spaces
    eAtom :: Descr f => f Expr -> f Expr
    eAtom exp = nonTerminal "atom" $
        aConst  orElse  eParens exp
    aConst :: Descr f => f Expr
    aConst = nonTerminal "const" $ Const . read <$> many1 digit
    eParens :: Descr f => f a -> f a
    eParens inner =
        id <$  char '('
           <*  spaces
           <*> inner
           <*  spaces
           <*  char ')'
           <*  spaces
    And indeed, this works:
    *Main> putStr $ ppGrammar "expr" parseExpr
    const = digit,  digit ;
    spaces =  ' '   newline ;
    atom = const   '(', spaces, expr, spaces, ')', spaces;
    mult = atom,  spaces, '*', spaces, atom , spaces;
    plus = mult,  spaces, '+', spaces, mult , spaces;
    expr = plus;

    Recursion (variant 2) Interestingly, there is another solution to this problem, which avoids introducing recNonTerminal and explicitly passing around the recursive call (i.e. the exp in the example). To implement that we have to adjust our Grammar type as follows:
    newtype Grammar a = G ([String] -> (BNF, RHS))
    The idea is that the list of strings is those non-terminals that we are currently defining. So in nonTerminal, we check if the non-terminal to be introduced is currently in the process of being defined, and then simply ignore the body. This way, the recursion is stopped automatically:
    nonTerminalG :: String -> (Grammar a) -> Grammar a
    nonTerminalG name (G g) = G $ \seen ->
        if name  elem  seen
        then ([], NonTerminal name)
        else let (prods, rhs) = g (name : seen)
             in (prods ++ [(name, rhs)], NonTerminal name)
    After adjusting the other primitives of Grammar (including the Functor and Applicative instances, wich now again have nonTerminal) to type-check again, we observe that this parser/grammar generator for expressions, with genuine recursion, works now:
    parseExp :: Descr f => f Expr
    parseExp = nonTerminal "expr" $
        ePlus
    ePlus :: Descr f => f Expr
    ePlus = nonTerminal "plus" $
        mkPlus <$> eMult
               <*> many (spaces *> char '+' *> spaces *> eMult)
               <*  spaces
    eMult :: Descr f => f Expr
    eMult = nonTerminal "mult" $
        mkPlus <$> eAtom
               <*> many (spaces *> char '*' *> spaces *> eAtom)
               <*  spaces
    eAtom :: Descr f => f Expr
    eAtom = nonTerminal "atom" $
        aConst  orElse  eParens parseExp
    Note that the recursion is only going to work if there is at least one call to nonTerminal somewhere around the recursive calls. We still cannot implement many as naively as above.

    Homework If you want to play more with this: The homework is to define a parser/grammar-generator for EBNF itself, as specified in this variant:
    identifier = letter,  letter   digit   '-' ;
    spaces =  ' '   newline ;
    quoted-char = non-quote-or-backslash   '\\', '\\'   '\\', '\'';
    terminal = '\'',  quoted-char , '\'', spaces;
    non-terminal = identifier, spaces;
    option = '[', spaces, rhs, spaces, ']', spaces;
    repetition = ' ', spaces, rhs, spaces, ' ', spaces;
    group = '(', spaces, rhs, spaces, ')', spaces;
    atom = terminal   non-terminal   option   repetition   group;
    sequence = atom,  spaces, ',', spaces, atom , spaces;
    choice = sequence,  spaces, ' ', spaces, sequence , spaces;
    rhs = choice;
    production = identifier, spaces, '=', spaces, rhs, ';', spaces;
    bnf = production,  production ;
    This grammar is set up so that the precedence of , and is correctly implemented: a , b c will parse as (a, b) c. In this syntax for BNF, terminal characters are quoted, i.e. inside ' ', a ' is replaced by \' and a \ is replaced by \\ this is done by the function quote in ppRHS. If you do this, you should able to round-trip with the pretty-printer, i.e. parse back what it wrote:
    *Main> let bnf1 = runGrammer "expr" parseExpr
    *Main> let bnf2 = runGrammer "expr" parseBNF
    *Main> let f = Data.Maybe.fromJust . parse parseBNF. ppBNF
    *Main> f bnf1 == bnf1
    True
    *Main> f bnf2 == bnf2
    True
    The last line is quite meta: We are using parseBNF as a parser on the pretty-printed grammar produced from interpreting parseBNF as a grammar.

    Conclusion We have again seen an example of the excellent support for abstraction in Haskell: Being able to define so very different things such as a parser and a grammar description with the same code is great. Type classes helped us here. Note that it was crucial that our combined parser/grammars are only able to use the methods of Applicative, and not Monad. Applicative is less powerful, so by giving less power to the user of our Descr interface, the other side, i.e. the implementation, can be more powerful. The reason why Applicative is ok, but Monad is not, is that in Applicative, the results do not affect the shape of the computation, whereas in Monad, the whole point of the bind operator (>>=) is that the result of the computation is used to decide the next computation. And while this is perfectly fine for a parser, it just makes no sense for a grammar generator, where there simply are no values around! We have also seen that a phantom type, namely the parameter of Grammar, can be useful, as it lets the type system make sure we do not write nonsense. For example, the type of orElseG ensures that both grammars that are combined here indeed describe something of the same type.

    1. It seems to be the week of applicative-appraising blog posts: Brent has posted a nice piece about enumerations using Applicative yesterday.
    2. I like how in this alignment of <*> and <* the > point out where the arguments are that are being passed to the function on the left.

    Dirk Eddelbuettel: Rblpapi 0.3.5

    A new release of Rblpapi is now on CRAN. Rblpapi provides a direct interface between R and the Bloomberg Terminal via the C++ API provided by Bloomberg Labs (but note that a valid Bloomberg license and installation is required). This is the sixth release since the package first appeared on CRAN last year. This release brings new functionality via new (getPortfolio()) and extended functions (getTicks()) as well as several fixes:

    Changes in Rblpapi version 0.3.5 (2016-10-25)
    • Add new function getPortfolio to retrieve portfolio data via bds (John in #176)
    • Extend getTicks() to (optionally) return non-numeric data as part of data.frame or data.table (Dirk in #200)
    • Similarly extend getMultipleTicks (Dirk in #202)
    • Correct statement on timestamp for getBars (Closes issue #192)
    • Minor edits to a few files in order to either please R(-devel) CMD check --as-cran, or update documentation

    Courtesy of CRANberries, there is also a diffstat report for the this release. As always, more detailed information is on the Rblpapi page. Questions, comments etc should go to the issue tickets system at the GitHub repo.

    This post by Dirk Eddelbuettel originated on his Thinking inside the box blog. Please report excessive re-aggregation in third-party for-profit settings.

    25 October 2016

    Laura Arjona Reina: Rankings, Condorcet and free software: Calculating the results for the Stretch Artwork Survey

    We had 12 candidates for the Debian Stretch Artwork and a survey was set up for allowing people to vote which one they prefer.

    The survey was run in my LimeSurvey instance, surveys.larjona.net. LimeSurvey its a nice free software with a lot of features. It provides a Ranking question type, and it was very easy for allowing people to vote in the Debian style (Debian uses the Condorcet method in its elections).

    However, although LimeSurvey offers statistics and even graphics to show the results of many type of questions, its output for the Ranking type is not useful, so I had to export the data and use another tool to find the winner.
    Export the data from LimeSurvey
    I ve created a read-only user to visit the survey site. With this visitor you can explore the survey questionnaire, its results, and export the data.
    Username: stretch
    Password: artwork
    First attempt, the quick and easy (and nonfree, I guess)
    There is an online tool to calculate the Condorcet winner, http://www.ericgorr.net/condorcet/
    The steps I followed to feed the tool with the data from LimeSurvey were these:
    1.- Went to admin interface of lime survey, selected the stretch artwork survey, responses and statistics, export results to application
    2.- Selected Completed responses only , Question codes , Answer codes , and exported to CSV. (results_stretch1.csv)
    3.- Opened the CSV with LibreOffice Calc, and removed these columns:
    id submitdate lastpage startlanguage
    4.- Remove the first row containing the headers and saved the result (results_stretch2.csv)
    5.- In commandline:
    sort results_stretch2.csv   uniq -c > results_stretch3.csv
    6.- Opened results_stretch3.csv with LibreOffice Calc and merge delimitors when importing.
    7.- Removed the first column (blank) and added a column between the numbers and the first ranked option, and fulfilled that column with : value. Saved (results_stretch4.csv)
    8.- Opened results_stretch4.csv with my preferred editor and search and replace ,:, for : and after that, search and replace , for > . Save the result (results_stretch5.csv)
    9.- Went to http://condorcet.ericgorr.net/, selected Condorcet basic, tell me some things , and pasted the contents of results_stretch5.csv there.
    The results are in results_stretch1.html
    But where is the source code of this Condorcet tool?
    I couldn t find the source code (nor license) of the solver by Eric Gorr.
    The tool is mentioned in http://www.accuratedemocracy.com/z_tools.htm where other tools are listed and when the tool is libre software, is noted so. But not in this case.
    There, I found another tool, VoteEngine, which is open source, so I tried with that.
    Second attempt: VoteEngine, a Free Open Source Software tool made with Python
    I used a modification of voteengine-0.99 (the original zip is available in http://vote.sourceforge.net/ and a diff with the changes I made (basically, Numeric -> numpy and Int -> int, inorder that works in Debian stable), here.
    Steps 1 to 4 are the same as in the first attempt.
    5.- Sorted alphabetically the different 12 options to vote, and
    assigned a letter to each one (saved the assignments in a file called
    6.- Opened results_stretch2.csv with my favorite editor, and search
    and replace the name of the different options, for their corresponding
    letter in stretch_key.txt file.
    Searched and replaced , for (space). Then, saved the results into
    7.- Copied the input.txt file from voteengine-0.99 into stretch.txt and edited the options
    to our needs. Pasted the contents of results_stretch3_voteengine.cvs
    at the end of stretch.txt
    8.-In the commandline
    ./voteengine.py <stretch.txt  > winner.txt
    (winner.txt contains the results for the Condorcet method).
    9.- I edited again stretch.txt to change the method to shulze and
    calculated the results, and again with the smith method. The winner in
    the 3 methods is the same. I pasted the summary of these 3 methods
    (shulze and smith provide a ranked list) in stretch_results.txt
    If it can be done, it can be done with R
    I found the algstat R package:
    which includes a condorcet function but I couldn t make it work with the data.
    I m not sure how the data needs to be shaped. I m sure that this can be done in R and the problem is me, in this case. Comments are welcome, and I ll try to ask to a friend whose R skills are better than mine!
    And another SaaS
    I found https://www.condorcet.vote/ and its source code. It would be interesting to deploy a local instance to drive future surveys, but for this time I didn t want to fight with PHP in order to use only the solver part, nor install another SaaS in my home server just to find that I need some other dependency or whatever.
    I ll keep an eye on this, though, because it looks like a modern and active project.
    Finally, devotee
    Well and which software Debian uses for its elections?
    There is a git repository with devotee, you can clone it:
    I found that although the tool is quite modular, it s written specifically for the Debian case (votes received by mail, GPG signed, there is a quorum, and other particularities) and I was not sure if I could use it with my data. It is written in Perl and then I understood it worse than the Python from VoteEngine.
    Maybe I ll return to it, though, when I have more time, to try to put our data in the shape of a typicall tally.txt file and then see if the module solving the condorcet winner can work for me.
    That s all, folks! (for now )
    Comments
    You can coment on this blog post in this pump.io thread

    Filed under: Tools Tagged: data mining, Debian, English, SaaS, statistics

    Jose M. Calhariz: New packages for Amanda on the works

    Because of the upgrade of perl, amanda is currently broken on testing and unstable on Debian. The problem is known and I am working with my sponsor to create new packages to solve the problem. Please hang a little more.

    Bits from Debian: "softWaves" will be the default theme for Debian 9

    The theme "softWaves" by Juliette Taka Belin has been selected as default theme for Debian 9 'stretch'. softWaves Login screen. Click to see the whole theme proposal After the Debian Desktop Team made the call for proposing themes, a total of twelve choices have been submitted, and any Debian contributor has received the opportunity to vote on them in a survey. We received 3,479 responses ranking the different choices, and softWaves has been the winner among them. We'd like to thank all the designers that have participated providing nice wallpapers and artwork for Debian 9, and encourage everybody interested in this area of Debian, to join the Design Team. It is being considered to package all of them so they are easily available in Debian. If you want to help in this effort, or package any other artwork (for example, particularly designed to be accessibility-friendly), please contact the Debian Desktop Team, but hurry up, because the freeze for new packages in the next release of Debian starts on January 5th, 2017. This is the second time that Debian ships a theme by Juliette Belin, who also created the theme "Lines" that enhances our actual stable release, Debian 8. Congratulations, Juliette, and thank you very much for your continued commitment to Debian!

    Julian Andres Klode: Introducing DNS66, a host blocker for Android

    ic_launcher I m proud (yes, really) to announce DNS66, my host/ad blocker for Android 5.0 and newer. It s been around since last Thursday on F-Droid, but it never really got a formal announcement. DNS66 creates a local VPN service on your Android device, and diverts all DNS traffic to it, possibly adding new DNS servers you can configure in its UI. It can use hosts files for blocking whole sets of hosts or you can just give it a domain name to block (or multiple hosts files/hosts). You can also whitelist individual hosts or entire files by adding them to the end of the list. When a host name is looked up, the query goes to the VPN which looks at the packet and responds with NXDOMAIN (non-existing domain) for hosts that are blocked. You can find DNS66 here: F-Droid is the recommended source to install from. DNS66 is licensed under the GNU GPL 3, or (mostly) any later version. Implementation Notes DNS66 s core logic is based on another project, dbrodie/AdBuster, which arguably has the cooler name. I translated that from Kotlin to Java, and cleaned up the implementation a bit: All work is done in a single thread by using poll() to detect when to read/write stuff. Each DNS request is sent via a new UDP socket, and poll() polls over all UDP sockets, a Device Socket (for the VPN s tun device) and a pipe (so we can interrupt the poll at any time by closing the pipe). We literally redirect your DNS servers. Meaning if your DNS server is 1.2.3.4, all traffic to 1.2.3.4 is routed to the VPN. The VPN only understands DNS traffic, though, so you might have trouble if your DNS server also happens to serve something else. I plan to change that at some point to emulate multiple DNS servers with fake IPs, but this was a first step to get it working with fallback: Android can now transparently fallback to other DNS servers without having to be aware that they are routed via the VPN. We also need to deal with timing out queries that we received no answer for: DNS66 stores the query into a LinkedHashMap and overrides the removeEldestEntry() method to remove the eldest entry if it is older than 10 seconds or there are more than 1024 pending queries. This means that it only times out up to one request per new request, but it eventually cleans up fine.
    Filed under: Android, Uncategorized

    Michal &#268;iha&#345;: New features on Hosted Weblate

    Today, new version has been deployed on Hosted Weblate. It brings many long requested features and enhancements. Adding project to watched got way simpler, you can now do it on the project page using watch button: Watch project Another feature which will be liked by project admins is that they can now change project metadata without contacting me. This works for both project and component level: Project settings And adding some fancy things, there is new badge showing status of translations into all languages. This is how it looks for Weblate itself: Translation status As you can see it can get pretty big for projects with many translations, but you get complete picture of the translation status in it. You can find all these features in upcoming Weblate 2.9 which should be released next week. Complete list of changes in Weblate 2.9 is described in our documentation.

    Filed under: Debian English phpMyAdmin SUSE Weblate 0 comments

    Jaldhar Vyas: Aaargh gcc 5.x You Suck

    Aaargh gcc 5.x You Suck I had to write a quick program today which is going to be run many thousands of times a day so it has to run fast. I decided to do it in c++ instead of the usual perl or javascript because it seemed appropriate and I've been playing around a lot with c++ lately trying to update my knowledge of its' modern features. So 200 LOC later I was almost done so I ran the program through valgrind a good habit I've been trying to instill. That's when I got a reminder of why I avoid c++.
    
    ==37698== HEAP SUMMARY:
    ==37698==     in use at exit: 72,704 bytes in 1 blocks
    ==37698==   total heap usage: 5 allocs, 4 frees, 84,655 bytes allocated
    ==37698== 
    ==37698== LEAK SUMMARY:
    ==37698==    definitely lost: 0 bytes in 0 blocks
    ==37698==    indirectly lost: 0 bytes in 0 blocks
    ==37698==      possibly lost: 0 bytes in 0 blocks
    ==37698==    still reachable: 72,704 bytes in 1 blocks
    ==37698==         suppressed: 0 bytes in 0 blocks
    
    One of things I've learnt which I've been trying to apply more rigorously is to avoid manual memory management (news/deletes.) as much as possible in favor of modern c++ features such as std::unique_ptr etc. By my estimation there should only be three places in my code where memory is allocated and none of them should leak. Where do the others come from? And why is there a missing free (or delete.) Now the good news is that valgrind is saying that the memory is not technically leaking. It is still reachable at exit but that's ok because the OS will reclaim it. But this program will run a lot and I think it could still lead to problems over time such as memory fragmentation so I wanted to understand what was going on. Not to mention the bad aesthetics of it. My first assumption (one which has served me well over the years) was to assume that I had screwed up somewhere. Or perhaps it could some behind the scenes compiler magic. It turned out to be the latter -- sort of as I found out only after two hours of jiggling code in different ways and googling for clues. That's when I found this Stack Overflow question which suggests that it is either a valgrind or compiler bug. The answer specifically mentions gcc 5.1. I was using Ubuntu LTS which has gcc 5.4 so I have just gone ahead and assumed all 5.x versions of gcc have this problem. Sure enough, compiling the same program on Debian stable which has gcc 4.9 gave this...
    
    ==6045== 
    ==6045== HEAP SUMMARY:
    ==6045==     in use at exit: 0 bytes in 0 blocks
    ==6045==   total heap usage: 3 allocs, 3 frees, 10,967 bytes allocated
    ==6045== 
    ==6045== All heap blocks were freed -- no leaks are possible
    ==6045== 
    
    ...Much better. The executable was substantially smaller too. The time was not a total loss however. I learned that valgrind is pronounced val-grinned (it's from Norse mythology.) not val-grind as I had thought. So I have that going for me which is nice.

    Russ Allbery: Review: Lord of Emperors

    Review: Lord of Emperors, by Guy Gavriel Kay
    Series: Sarantine Mosaic #2
    Publisher: Eos
    Copyright: 2000
    Printing: February 2001
    ISBN: 0-06-102002-8
    Format: Mass market
    Pages: 560
    Lord of Emperors is the second half of a work that began with Sailing to Sarantium and is best thought of as a single book split for publishing reasons. You want to read the two together and in order. As is typical for this sort of two-part work, it's difficult to review the second half without spoilers. I'll be more vague about the plot and the characters than normal, and will mark one bit that's arguably a bit of a spoiler (although I don't think it would affect the enjoyment of the book). At the end of Sailing to Sarantium, we left Crispin in the great city, oddly and surprisingly entangled with some frighteningly powerful people and some more mundane ones (insofar as anyone is mundane in a Guy Gavriel Kay novel, but more on that in a bit). The opening of Lord of Emperors takes a break from the city to introduce a new people, the Bassanids, and a new character, Rustem of Karakek. While Crispin is still the heart of this story, the thread that binds the entirety of the Sarantine Mosaic together, Rustem is the primary protagonist for much of this book. I had somehow forgotten him completely since my first read of this series many years ago. I have no idea how. I mentioned in my review of the previous book that one of the joys of reading this series is competence porn: watching the work of someone who is extremely good at what they do, and experiencing vicariously some of the passion and satisfaction they have for their work. Kay's handling of Crispin's mosaics is still the highlight of the series for me, but Rustem's medical practice (and Strumosus, and the chariot races) comes close. Rustem is a brilliant doctor by the standards of the time, utterly frustrated with the incompetence of the Sarantine doctors, but also weaving his own culture's belief in omens and portents into his actions. He's more reserved, more laconic than Crispin, but is another character with focused expertise and a deep internal sense of honor, swept unexpectedly into broader affairs and attempting to navigate them by doing the right thing in each moment. Kay fills this book with people like that, and it's compelling reading. Rustem's entrance into the city accidentally sets off a complex chain of events that draws together all of the major characters of Sailing to Sarantium and adds a few more. The stakes are no less than war and control of major empires, and here Kay departs firmly from recorded history into his own creation. I had mentioned in the previous review that Justinian and Theodora are the clear inspirations for this story; that remains true, and many other characters are easy to map, but don't expect history to go here the way that it did in our world. Kay's version diverges significantly, and dramatically. But one of the things I love the most about this book is its focus on the individual acts of courage, empathy, and ethics of each of the characters, even when those acts do not change the course of empires. The palace intrigue happens, and is important, but the individual acts of Kay's large cast get just as much epic narrative attention even if they would never appear in a history book. The most globally significant moment of the book is not the most stirring; that happens slightly earlier, in a chariot race destined to be forgotten by history. And the most touching moment of the book is a moment of connection between two people who would never appear in history, over the life of a third, that matters so much to the reader only because of the careful attention to individual lives and personalities Kay has shown over the course of a hundreds of pages. A minor spoiler follows in the next paragraph, although I don't think it affects the reading of the book. One brilliant part of Kay's fiction is that he doesn't have many villains, and goes to some lengths to humanize the actions of nearly everyone in the book. But sometimes the author's deep dislike of one particular character shows through, and here it's Pertennius (the clear analogue of Procopius). In a way, one could say the entirety of the Sarantine Mosaic is a rebuttal of the Secret History. But I think Kay's contrast between Crispin's art (and Scortius's, and Strumosus's) and Pertennius's history has a deeper thematic goal. I came away from this book feeling like the Sarantine Mosaic as a whole stands in contrast to a traditional history, stands against a reduction of people to dates and wars and buildings and governments. Crispin's greatest work attempts to capture emotion, awe, and an inner life. The endlessly complex human relationships shown in this book running beneath the political events occasionally surface in dramatic upheavals, but in Kay's telling the ones that stay below the surface are just as important. And while much of the other art shown in this book differs from Crispin's in being inherently ephemeral, it shares that quality of being the art of life, of complexity, of people in dynamic, changing, situational understanding of the world, exercising competence in some area that may or may not be remembered. Kay raises to the level of epic the bits of history that don't get recorded, and, in his grand and self-conscious fantasy epic style, encourages the reader to feel those just as deeply as the ones that will have later historical significance. The measure of people, their true inner selves, is often shown in moments that Pertennius would dismiss and consider unworthy of recording in his history. End minor spoiler. I think Lord of Emperors is the best part of the Sarantine Mosaic duology. It keeps the same deeply enjoyable view of people doing things they are extremely good at while correcting some of the structural issues in the previous book. Kay continues to use a large cast, and continues to cut between viewpoint characters to show each event from multiple angles, but he has a better grasp of timing and order here than in Sailing to Sarantium. I never got confused about the timeline, thanks in part to more frequent and more linear scene cuts. And Lord of Emperors passes, with flying colors, the hardest test of a novel with a huge number of viewpoint characters: when Kay cuts to a new viewpoint, my reaction is almost always "yes, I wanted to see what they were thinking!" and almost never "wait, no, go back!". My other main complaint about Sailing to Sarantium was the treatment of women, specifically the irresistibility of female sexual allure. Kay thankfully tones that down a lot here. His treatment of women is still a bit odd one notices that five women seem to all touch the lives of the same men, and little room is left for Platonic friendship between the genders but they're somewhat less persistently sexualized. And the women get a great deal of agency in this book, and a great deal of narrative respect. That said, Lord of Emperors is also emotionally brutal. It's beautifully done, and entirely appropriate to the story, and Kay does provide a denouement that takes away a bit of the sting. But it's still very hard to read in spots if you become as invested in the characters and in the world as I do. Kay is writing epic that borders on tragedy, and uses his full capabilities as a writer to make the reader feel it. I love it, but it's not a book that I want to read too often. As with nearly all Kay, the Sarantine Mosaic as a whole is intentional, deliberate epic writing, wearing its technique on its sleeve and making no apologies. There is constant foreshadowing, constant attempts to draw larger conclusions or reveal great principles of human nature, and a very open, repeated stress on the greatness and importance of events while they're being described. This works for me, but it doesn't work for everyone. If it doesn't work for you, the Sarantine Mosaic is unlikely to change your mind. But if you're in the mood for that type of story, I think this is one of Kay's best, and Lord of Emperors is the best half of the book. Rating: 10 out of 10

    Next.

    Previous.