Friday, May 05, 2017

Chunked Enumerable revisited (in F#)

Revisiting an old post on the subject, prompted by a recent question on the MSDN Visual F# forum.

Despite it being a very common thing to want to do, to process a stream of data in blocks, there isn't a library function for it yet; and it's not something that can be put together in a functional form using the pieces we have in the Collections.Seq module. Either, as in my earlier attempts, we have to go into object-land, with intrusions into the implementation details of enumerations (and their disposal), and eager evaluation at best; or we have to go imperative/procedural to get full lazy evaluation as per the code buried within the this C# example.

For idiomatic F#, we don't want an extension method, but a stand-alone function which we can chain into the usual pipeline of |> operations, so we would translate it thus —

where we bury the imperative/mutable kernel, the place where we count the number of steps we take (or stop early when we hit the end of the input), inside an inner function.

Monday, May 01, 2017

April Cycling

Up to 14655.9, 274.0, 142.1 = 173.2 miles, which is about the same as the monthly rate I averaged over Q1 (YTD 714 miles). Way down on last year; I didn't even keep up the 30 Days challenge, as the 4th was wet in the afternoon/early evening. When we have had good weather, I've been more inclined to work in the garden, where it's sheltered from the rather chill winds that characterised most of the month or get things done around the house; and it's less draining to start early, drive, finish early than cycle, work, cycle, work some more, cook. What rain we have had in a generally dry month also managed to skew towards driving and staying dry on days where I might normally have cycled; and these days, the chance to stop in town on the way home has reduced the need or incentive to cycle into town at weekends.

Saturday, April 01, 2017

March Cycling

Up to 14655.9, 148.8, 142.1 = 193.6 miles + ~90 off-meter = 284, which is better than January and February combined (YTD 541 miles), with more commuting and shopping by bike, and in the good weather at the end of the month, another quick CycleBreaks holiday and the first days of cycling with bare arms for the year (even if a chill easterly kept bringing in haze from the North Sea).

Sunday, being the day after the clocks went even an early start was late by the clock, and being Mothering Sunday to boot, finding lunch as well as the usual getting the saddle height just right and getting used to the bike meant I ended up doing a fairly short ride, during which time the cloud and wind picked up, and encouraged me back to base. Monday started grey, but by the time I'd gone most of the way through Ipswich the sun was burning through, so I could take the jacket off for the full circuit of Alton Water and then back again. Tuesday was misty until late morning, but I managed a ride out to Snape and back after it started to clear, with a pit-stop at the Elephant and Castle in Eyke on the way back.

Without an on-board measure this time, I estimated the journeys on Google Maps, and it was quite a surprise to find that the "short" ride to Snape and back was about the same distance as the "long" loop of Alton Water that took me about 50% longer in terms of time in the saddle.

Tuesday, February 28, 2017

February Cycling

Up to 14625.1, 0, 128.1 -- 147.6 miles, which is better than January, since I did manage to average more than one day a week commuting by bike, and the weather has at times felt positively spring-like. No rides just for pure pleasure, as yet, but definitely more inclination to go by bike for business.

Saturday, February 25, 2017

Powershell Transcript cmdlets and secondary runspaces gotcha

So, I had some fun this past week, with a piece of code that, stripped to its essentials, looked like

which yields

Stop-Transcript : An error occurred stopping transcription: The host is not currently transcribing.
At line:1 char:1
+ Stop-Transcript
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Stop-Transcript], PSInvalidOperationException
    + FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.StopTranscriptCommand

with a transcript (again, stripped to its essentials) of

Transcript started, output file is ...
PS>$pool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1,1)
PS># do stuff...
Windows PowerShell transcript end

with nothing after the Close() showing up.

It turns out that, however you create a runspace, even if using an instance of a custom PSHost subclass and explicitly minting one through CreateRunspace(), the runspace will still get attached to an internal PSHost subtype, which couples it to a whole web of other internal and/or sealed types, eventually linking it to the transcription state of the overall PowerShell session. And when a runspace closes, it closes all open transcripts attached to it.


Fortunately, there is one public API available to us that can sever this link, and one that makes a perverse sort of sense, after you've run through all the plumbing:

which finishes with

Transcript stopped, output file is...

and a transcript that looks like

Transcript started, output file is ...

PS>$pool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1,1)
PS>$save = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace
PS>try {
    [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace = $null
    # do stuff...
    # do more stuff...
finally {
    [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace = $save
Windows PowerShell transcript end

because it turns out that, via a long chain of indirections, it is the -- fortunately thread-static -- global default runspace which is the thing that contaminates our intended-to-be-isolated worker environment.

Now, it would be understandable if runspace construction were to directly use the default runspace as a prototype, but it's nothing so obvious. It actually comes in via the UI object that is tenuously attached to the runspace reaching out to the default runspace. That's not so good, and speaks of excessive internal coupling.

Checking metrics on the assembly System.Management.Automation, we can see that it is indeed highly internally coupled, with a relational cohesion of 7.22, which is a level that is not so much coherent as positively incestuous. So while I didn't spot any other obvious booby-traps waiting to be sprung, I'm sure there are others that will rise up and bite the occasional edge-case.

Sunday, February 05, 2017

SHA256.Create() and "Works on my machine" FIPS-compliance

I've just had an interesting run-in with a little-advertised and not backwards-compatible feature in .net 4.6 and up, and how it affects FIPS-compliance, for those of us who have to worry about such things.

You see, in the recent .net versions (unlike the older ones), the selection of the default implementation toggles on whether the machine has HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy\[Enabled] set non-zero or not (or the equivalent group policy), and will select a FIPS-certified implementation for SHA256 (and SHA384 and SHA512), and, interestingly only for those flavours of SHA-2, using exactly the same criterion as is used to make the non-compliant implementations raise an exception on construction. So, assuming no app or machine level overrides, the matrix looks like this:

no FIPS enforcementFIPS enforced
.net 4.6 and up

(Dev machine)


"Works on my machine."
.net 4.5.2 or earlier

(Slow-moving customer environment)



The end-stop selection of algorithm defaults (where not overridden by a specific [class].Create()) in .net 4.5.2 are drawn from mscorlib alone, many FIPS-compliant, with the notable exceptions being the "new" (SHA-2, or AES which is present only by the original name of Rijndael), or old-and-deprecated (like MD5) algorithms. As in most cases, [DerivedClass].Create() is just a synonym for [BaseAlgorithm].Create(), you can get a false sense of security here -- SHA256CryptoServiceProvider.Create() will equally spit out an instance of SHA256Managed in three of the cases above, and SHA256Cng in the fourth ("works on my machine").

TL;DR -- if you have to worry about FIPS, don't use [Algorithm].Create(), but select the one you mean by calling its constructor explicitly.

Wednesday, February 01, 2017

January Cycling

Up to 14477.7, 0, 127.9 -- 108.7 + 0 + 1.8 = 109.5 miles in a chilly month, with a lot of days where I might have cycled blocked out by other things, so I only ended up commuting once a week, with one shopping trip and one expedition for fresh air on the New Year Bank Holiday. That may be well less than I did last year, but this time I'm not feeling ground down by the cycle-work-cycle-work-cook-eat-sleep routine I was caught in back then.

Sunday, January 22, 2017

Yet another thread 'main' panicked Rust gotcha -- mind your

Picking up the little Rust project I'd set aside in late summer, with the latest release, I got

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { repr: Os { code: 2, message: "The system
 cannot find the file specified." } }', ../src/libcore\

Setting environment variable RUST_BACKTRACE to 1, and retrying gave

stack backtrace:
   0:   0xf314b7 - std::panicking::default_hook
                at C:\bot\slave\stable-dist-rustc-win-msvc-32\build\src\libstd\
   1:   0xf31caa - std::panicking::rust_panic_with_hook
                at C:\bot\slave\stable-dist-rustc-win-msvc-32\build\src\libstd\
   2:   0xf31b47 - std::panicking::begin_panic<collections::string::String>
                at C:\bot\slave\stable-dist-rustc-win-msvc-32\build\src\libstd\
   3:   0xf31a86 - std::panicking::begin_panic_fmt
                at C:\bot\slave\stable-dist-rustc-win-msvc-32\build\src\libstd\
   4:   0xf319e2 - std::panicking::rust_begin_panic
                at C:\bot\slave\stable-dist-rustc-win-msvc-32\build\src\libstd\
   5:   0xf36f82 - core::panicking::panic_fmt
                at C:\bot\slave\stable-dist-rustc-win-msvc-32\build\src\libcore\
   6:   0xf22402 - core::result::unwrap_failed<std::io::error::Error>
                at C:\bot\slave\stable-dist-rustc-win-msvc-32\build\src\libcore\
   7:   0xf21d0f - core::result::Result<std::process::ExitStatus, std::io::error::Error>::unwrap<std::process::ExitStatu
                at C:\bot\slave\stable-dist-rustc-win-msvc-32\build\src\libcore\
   8:   0xf23b5e - build_script_build::main
                at c:\Users\steve\hg\Projects\rust\HelloWorld\
   9:   0xf3556b - panic_unwind::__rust_maybe_catch_panic
                at C:\bot\slave\stable-dist-rustc-win-msvc-32\build\src\libpanic_unwind\
  10:   0xf322f0 - std::rt::lang_start
                at C:\bot\slave\stable-dist-rustc-win-msvc-32\build\src\libstd\
  11:   0xf23d01 - main
  12:   0xf39768 - __scrt_common_main_seh
                at f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:253
  13: 0x75168e93 - BaseThreadInitThunk
  14: 0x772d9bc2 - RtlDestroyQueryDebugBuffer

which wasn't particularly helpful.

Resolution was an inspired guess -- my contained a path which ran a command by explicit path into the windows SDK for an x64 machine, when I was trying on an old 32-bit box.

Fixing the path to be selected according to whether the file actually exists resolved the issue.

Thursday, January 19, 2017

…and good riddance!

I took this screen-shot in mid-October 2007, well before the primary season that propelled the junior Senator from Illinois onto the national stage--

Today, I have a use for it, at last!

Wednesday, January 04, 2017

C# under the covers III

So, you have some code that looks like this

    private static bool Match(int item, int? target)
        if (target.HasValue)
            return item == target;

        return false;

How many tests do you need to write to get 100% branch coverage?

If you answered "two -- one with a value, one without", you'd be as surprised as I was when I tried it.

It turns out that the implicit extraction of the value of the nullable value contains its own HasValue check, and the IL looks like

IL_0000: nop
IL_0001: ldarga.s target
IL_0003: call instance bool valuetype [mscorlib]System.Nullable\u00601::get_HasValue()
IL_0008: ldc.i4.0
IL_0009: ceq
IL_000b: stloc.1
IL_000c: ldloc.1
IL_000d: brtrue.s IL_002b

IL_000f: nop
IL_0010: ldarg.0
IL_0011: stloc.2
IL_0012: ldarg.1
IL_0013: stloc.3
IL_0014: ldloc.2
IL_0015: ldloca.s CS$0$0003
IL_0017: call instance !0 valuetype [mscorlib]System.Nullable\u00601::GetValueOrDefault()
IL_001c: bne.un.s IL_0027

IL_001e: ldloca.s CS$0$0003
IL_0020: call instance bool valuetype [mscorlib]System.Nullable\u00601::get_HasValue()
IL_0025: br.s IL_0028

IL_0027: ldc.i4.0

IL_0028: stloc.0
IL_0029: br.s IL_002f

IL_002b: ldc.i4.0
IL_002c: stloc.0
IL_002d: br.s IL_002f

IL_002f: ldloc.0
IL_0030: ret

Roughly, it goes "get a value, or the default, and test that; if not equal, accept that, otherwise only accept the equality if the nullable had a value."

If you write

    private static bool Match(int item, int? target)
        if (target.HasValue)
            return item == target.Value;

        return false;

then there is no compiler-generated branch for you to be caught by -- and it's probably slightly better coding practise, anyway.

Monday, January 02, 2017

Anime — 2016 in review

In the unfinished business area, I carried on with Symphogear G, which, after a cour and a half at last managed cohere to enough to deliver a decent number of set pieces in the last half-dozen episodes, and Bikki-tan is starting to get the hang of this magical girl business, including a closing move worthy of the White Devil herself. It was the other characters that needed a bit of a talking to, for a change -- "Excuse me, ladies, but why not postpone the emotional reunion scene until after you've stopped the bad guy who's slinking away stage right?"

All in all, trashy fun with cute girls finally figuring out how to punch everything into daijoubu.

I then continued on to Symphogear GX which was an odd mess -- the fights are cool enough, but Hibiki's hard reset every season is getting old -- by now she should be confident in dealing out the Power of Friendship ("with a Determination to Fist", as the season subtitle puts it). While it was not overall as bad as some of the detractors make out, it somehow managed to make what ought to have been a spectacular finale come over as just "meh" -- which is really the problem with the franchise as a whole. It ought to be something that is full of "Hell, yes!" moments, but those end up being restricted to the first episode of the season only; a measure of how flat-footed the delivery is.

One of the things I got as a Christmas 2015 present, for watching during the Winter season (where none of the airing titles grabbed) was the complete Nadia : The Secret of Blue Water, when we watched the first couple of episodes, they were absolutely charming. Despite being a series that gets talked about regularly, it felt like I was coming to the series quite unspoiled, which was an extra bonus.

As time went on, it went to some strange places, especially watched in proximity to Symphogear, where the two apparently very different series display a bizarre overlap in motifs (Finé clearly had something to do with Gargoyle's use of heretical technology). After that arc, though, during the Nautilus episodes, the series began to feel more and more like a French made-for-children series (rather than the "all ages" appeal which even toy commercials like Mobile Suit Gundam managed), and there was much less pull to watch it, especially as we moved into the Spring season and suddenly there were new airing series that caught my attention.

Plus, at past the half-way mark, I still wasn't feeling it -- the characters are irritating at best, fading into just ciphers at worst. Even with a detour past the notorious island and Africa arcs, or even if the quality had been maintained, I was still in the frame of mind to be eyeing the fast forwards button by that point, though I didn't feel as betrayed by it as in other similar cases, such as Madoka. Currently stalled with 4 episodes to go.

On the pure Gainax side, if Electra is a proto-Ritsuko, then Sanson is clearly a proto-Kamina; and Nadia is just an annoying little madam. At least with that as a trial run, NGE handled the "kids caught up in the adults' conspiracy" set-up way more deftly.

Of the spring series, there were three I actually watched through --

  • JJBA : Diamond is not Crash, which provided fabulous Saturday morning cartoons for the rest of the year, fitting surprisingly well into 3 cours despite being many chapters longer than Stardust Crusaders
  • Gundam Unicorn Re:0096, an edit of the earlier OVA into a 2-cour TV show -- plenty of UC Gundam nostalgia, but boy, was that ever a poster-child for "Japan doesn't into endings"! -- having bigged-up Laplace's Box beyond anything reasonable, topping it off with a 2001-style lightshow and Char Full Frontal channelling Keynes and then ... anti-climax when spending just a couple of minutes on an epilogue could have helped, to save it from being full of sound and fury but ultimately seeming to signify nothing; and
  • Flying Witch which turned into the surprise hit of the year, a relaxed, slightly magical, slice of life, the best in the style to come along since Aria, with only a minimum of school intruding on the lives of the teenage characters (the banal effect of the default setting showed in the weak and cliché driven

    > at school
    > anime girl can't cook

    episode that felt phoned in from some other SoL, with even the rather over-magical anime-original final episode being better). I did, however, find the episode where they weeded a substantial plot to bare earth in just a few hours, with no magical assistance, to be a bit stretching of the suspension of disbelief. Props also for having a boy in the cast who isn't any sort of romance self-insert.

There were also a bunch of tried-and-dropped-

  • Phantom World where I watched episode 1, to see the notorious limbo dance in context ... and it's all irritating fanservice all the time, peaking in the bit where MC trips and doesn't FOOSH.
  • Space Patrol Luluco lasted 4 short episodes before the attempt to mix DORAMA and LOLPlot into the off-the-wall stream of consciousness insanity like Inferno Cop was told me it was time to stop.
  • Hai Furi which had promise of being the new Girls und Panzer just went somewhere else. It could have done with just a brief explanation of what the ostensible purpose of the Blue Mermaids is by way of scene setting (not an extended exposition, just something quick and to the point like GaruPan introduced sensha-do), pulling forwards material from the second episode before going about undermining the superficial appearance of being just "cute sailing girls doing cute sailing things". Alas, that mishandling was a sign of things to come, and I dropped at the oh-so-hilarious "we're out of bog rolls" episode.
  • Anne Happy, with its fairly off-the-wall premise, was more entertaining than most of the CGDCT shows that I've tried (they usually feel like a chore to wade through even a first episode), but alas it spiralled through surreal into just downright stupid in very short order.

Summer opened with the promise that Amanchu would take on the cozy/comfy niche, being from the same mangaka as Aria, but it came off the worse in the immediate shift of gears and scenery -- and a lot more school than I'd expected. Flying Witch was comfy, this is irritating, with the "aren't they quirky? Look!!" being played up too much, like Pikari being in face-fault mode so much that when she has a normal face it just looks like "who is this different character? There was a only whistle-blowing muppet around just now" instead. Overall, Pikari is way too over-caffeinated genki, the mascot character Cha is just as bad as President Aria, and Teko is just a doormat.

Two episodes were quite enough -- the figure hugging outfits and fetish boots don't go anywhere near far enough to rescue this from disappointment of the year status.

I watched the "behind the scenes" episode 0 of the Taiwanese puppet show Thunderbolt Fantasy. Having grown up on Supermarionation™, it was both weirdly familiar and at the same time subtly awry (they don't have any strings!) -- but it looks worth going on with, so in the queue for now.

Autumn brought the first episode of The Ancient Magus' Bride OVA which shows promise -- just so long as it's not too much concentrated on the "little girl suffering for being the nail that stands out" part, and is more on the magical Natsume/Mushishi-like side of being able to see the youkai. With the opening shots of Big Ben and fog on the Thames, I was expecting something Victorian -- but then, suddenly, there's the Shard! -- even if the rural English framing does have some "Lol! Japan" touches.

I also sampled the first episode of Flip Flappers, and it felt like FLCL meets Daicon IV in The New World. Papika (the Daicon girl/Haruko fusion in the mix) is one of those infuriating brain dead genki girls just like Pikari, and around her the art style of the show wobbles from Imaishi-style wackiness, to acid flashbacks to Shin Sekai Yori in the respites between the manic bursts. Verdict -- Dropped like a colony.

Faring better, Izetta the Last Witch -- the BUNBUN sameface is quite strong in this one, with Nogi Wakaba-hime getting rescued by Takashima Yuuna in the first episode. Queued for later.

And half-way through ClassicaLoid, which is an odd mix of the whimsical and the dumb -- including the electro-pop renderings of various popular classics. Where it sticks to being SoL/comedy plus rework classical music videos, it's amusing fluff -- and so far it doesn't seem to be suffering too much from the vague plot. Ongoing.

The last couple of months have been marked by a lot of raiding the archives.

It started when, on a whim, I went back and resumed watching Tsuritama (that fishing anime from 4 years ago). First time around, I watched the first episode and said "No thanks!" to the instances of the MC sperging out, however uniquely portrayed, expecting that to be an ongoing thing the whole while. Starting again at episode 2, I was pleased to find that it got throttled back to the occasional goofy face, and the slice-of-life aspects carried it. And even when the plot emerged at about the halfway mark, it didn't do it in a massive change of gear (as so often happens). Even more surprisingly, that deft handling carried through the climax, to an actual satisfying conclusion, and as I remembered from the first time around, I liked the art style.

I then picked up IdolM@cross 7 AKB0048, notionally a promo thing for the AKB48 idol group, but under the influence of Shoji Kawamori (Macross, Aquarion) it cuts the mundane idol shenanigans with mech battles and quasi-magical girl action, under a premise of cute girls doing idol things IN SPAAAACCCCEE! vs the No Fun Police. It was fun enough to follow through to the end, though I'm not sure that I would have kept up with it on a "while airing" basis. The second cour, in particular, sagged a bit in the middle, and the ending was not as good as it could have been -- the opening of what was supposed to be the most impressive live show of all time (in order to activate the plot) was one of the limpest of the AKB numbers used, weakly delivered, and not a patch on their first appearance in the very first episode. Even hauling out the first OP number for the climax was below its use at the end of the first cour, added pyrotechnics notwithstanding.

The ending was not a very tidy one -- leaving an unexplained death, ambiguity as to exactly how along the scale through "A New Hope" to "Return of the Idols" the idol rebellion has come, and one of the in-group character conflicts completely shelved. And given how the show turned out, with only minor adjustments, the use of 3DCG in the group dancing scenes could have been given an in-story justification, as manifesting the original AKB.

Paranoia Agent, at least after 3 episodes, comes over as rather more tedious than I recall, a problem of knowing what is going on in broad that removes the "WTF is going on?" appeal. By contrast, on a third go around, Mouryou no Hako is being even more rewarding, when you can appreciate all the clues laid in plain sight, and the sheer density of the storytelling, and I gulped down the first five episodes in one sitting.

And for a surreal experience, I watched the first episodes of Yuri Bear Storm and Penguindrum one after the other -- so clearly the work of the same man, with all the common stylistic quirks. The former, alas, failed the 3 episode test, being pretty, but very, very tedious, and the stock-footage fanservice doesn't help it, either. I'm not inclined to continue just to find out whether a kitten gets killed at any time in this series, or if Ikuni has given up his Kaworu act.

As a change of pace, I watched the first couple of episodes of Twintail ni narimasu, which were amusing enough trash that I might watch some more, though it doesn't look like I'll be watching it on the strength of the fight choreography.

Finally, I've started my approximately every 4 year rewatch of Neon Genesis Evangelion (last done in the aftermath of the release of Q in theatre in Japan), currently up to episode 6. The material is familiar, but with enough time passing, the details blur into summary, so I can watch with as near new eyes as I can ever manage -- for example, this time around I actually noticed and WTF'd at the blooper about Misato's arrival at the station in episode 4. For the first time I'm watching the Platinum version, rather than the original OA DVDs, which gives the occasional WTF just in itself (the changes in the episode titles, mainly).

Then, with the original fresh in mind, to round off the year, I rewatched Rebuild 1.01, which was indeed a rather more downbeat take on the same material as episodes 1-6, and it wasn't just that the gloomy lighting balance in that version had stuck in my memory -- not only was Re!Shinji more of an introspective misery-guts than the original, even getting in an early hell-train ride, but so were practically everyone else, and the No Fun Police had gone through and removed any of the levity aside from the toothpicks scene, in favour of splashy spectacle.

Sunday, January 01, 2017

Books of 2016

I have a substantial stack of unread books that I've accumulated over the year, because some time in the early part of '16, I got an "if you like Glen Cook, you'll like this" recommendation for Steven Erikson's Gardens of the Moon, and I got sucked into the whole ten volume saga of which that was just the first.

It's addictive, in the sense that there's never any good point to stop, because something is always about to go down in one or other of the many interwoven strands of the narrative, just in time for a "meanwhile, on another continent, half a world away" to put everything in one theatre on hold as we move between books. With book 5 that gets taken to extremes. A character gets introduced at the start of book 4, and their strand of the story has them saying from time to time about it being useful to explain the predicament they were found in, and ends that book getting to a significant place and about to tell their tale -- and book 5 turns out to be that story, starting "long ago, on a continent far, far away,...". And in the scope of it all, times when apparently abandoned plot threads suddenly burst upon the scene again thousands of pages later

Now, 10,000+ pages, hundreds of characters, dozens of battles, many real-time months (only finished in the last few days, with it being my sole fiction reading all year), and several continents later, each volume about 1000 pages of set-up and 100 where the dominoes start to fall, there was a reasonable end to the saga, which I hadn't been expecting when I started the final volume. It should not come as a surprise that few of the characters we meet at the start survive until the end, though for some of those who don't, death, it turns out, is just another career move.

Also, unsurprisingly, there is a degree of padding and waffling, with characters engaging in somewhat sophomoric philosophising, which had me thinking "Oh, come on!" and "Get on with it, for goodness' sake!"; and a fair amount of building up minor characters simply so they can be given a tragic death scene, or can accidentally stumble into the way of some other more powerful actor and screw things up for them.

Still, it was entertaining enough for me to read through for all these months, and I applaud it for its ability to give a feeling of deep mythological time, and in eschewing the all-too-common fantasy clichés -- there are dragons as an ancient threat, but the nearest thing it has to a standard non-human race are the sort-of Dark Elves, which are more like Melnibonéans (in one case) or Vikings (in another) than anything else; the various ogre-ish/demi-giant types don't slot into obvious Monster Manual niches, and the dino-birds with the mole-rat social structure that form a threat that bubbles under for several volumes are definitely their own thing. And you can assume that the main participant race is not-quite-human either, given that the unisex armies actually function.

There are occasional points where the "from notes I was making for a tabletop RPG" origin slightly leaks through, but never offensively so, and occasionally as self-aware humour (the shop in not-AnkhMorpork that sells short lengths of rope and 10 foot poles, for example).