{"id":7616,"date":"2017-07-17T00:09:19","date_gmt":"2017-07-17T04:09:19","guid":{"rendered":"http:\/\/esr.ibiblio.org\/?p=7616"},"modified":"2017-07-17T04:51:34","modified_gmt":"2017-07-17T08:51:34","slug":"a-teaching-story","status":"publish","type":"post","link":"http:\/\/esr.ibiblio.org\/?p=7616","title":{"rendered":"A teaching story"},"content":{"rendered":"<p>The craft of programming is not a thing easily taught.  It&#8217;s not so much that the low level details like language syntaxes are difficult to convey, it&#8217;s more that (as I&#8217;ve written before) &#8220;the way of the hacker is a posture of mind&#8221;.<\/p>\n<p>The posture of mind is more essential than the details. I only know one way to teach that, and it looks like this&#8230;<\/p>\n<p><!--more--><\/p>\n<p>19:51:23 <b>esr<\/b> | You know, at some point you should build <a href=\"http:\/\/www.catb.org\/esr\/open-adventure\/\">Open Adventure<\/a> and play it. For that geek heritage experience, like admiring Classical temple friezes.<\/p>\n<p>ianbruene | note to self: play advent<\/p>\n<p>19:53:56 <b>esr<\/b> | I actually think this should count as (a very minor) part of your training, though I&#8217;m not sure I can fully articulate why.  Mumble mumble something mimesis and mindset. It was written by two guys with the mindset of <em>great<\/em> hackers. If playing that game gets you inside their heads even a <em>little<\/em> bit, you&#8217;ll have gained value&#8230;<\/p>\n<p>19:55:08  <b>ianbruene<\/b> | I already had a flag set to fix the non-human-readable save problem someday, if no one else got to it first. Kind of hard to do that without playing at least *some* of the game.<\/p>\n<p>19:55:15 <b>esr<\/b> | :-)<\/p>\n<p>19:55:29 <b>ianbruene<\/b> | (non human readable saves irk me)<\/p>\n<p>19:55:35 <b>esr<\/b> | An excellently chosen exercise, apprentice!<\/p>\n<p>19:57:57 <b>esr<\/b> | Since you&#8217;ve brought it up, let&#8217;s think through some of the tradeoffs.<\/p>\n<p>19:58:16 <b>ianbruene<\/b> |<\/p>\n<p>19:59:25 <b>esr<\/b> | You are right, in general, to dislike non-eyeball-friendly save formats, especially for small-scale applications.<\/p>\n<p>19:59:54 <b>esr<\/b> | But: There are two reasons to like the way it&#8217;s done now.<\/p>\n<p>20:00:27 <b>esr<\/b> | Care to guess at them?  You can ask me questions about the code context to develop your answer.<\/p>\n<p>20:00:12 <b>ianbruene<\/b> | anti-cheat being the obvious one<\/p>\n<p>20:00:48 * | <b>ianbruene<\/b> pulls up advent page<\/p>\n<p>20:00:51 <b>esr<\/b> | Right &#8211; it&#8217;s a low barrier, but not a completely useless one.<\/p>\n<p>20:02:02 <b>esr<\/b> | That is, it hits a nice sweet spot between &#8220;too easily editable&#8221; and &#8220;putting in extra work for something that in principle can always be circumvented no matter what you do&#8221;.<\/p>\n<p>20:02:19 <b>esr<\/b> | Speed bump.<\/p>\n<p>20:02:31 <b>esr<\/b> | That&#8217;s reason #1. What&#8217;s #2?<\/p>\n<p>20:02:49 <b>esr<\/b> | (Don&#8217;t overthink it.)<\/p>\n<p>20:03:16 <b>ianbruene<\/b> | ok, just seeing the first couple functions the comments mention twiddling a few things to prevent savescumming. falls under anti-cheat I assume?<\/p>\n<p>20:03:24 <b>esr<\/b> | Yes.<\/p>\n<p>20:03:44 <b>esr<\/b> | No, there&#8217;s something else much more basic.<\/p>\n<p>20:04:29  <b>ianbruene<\/b> | um&#8230;. well restore() is just a memcopy&#8230;&#8230;. that seems almost too simple<\/p>\n<p>20:04:41 <b>esr<\/b> | You&#8217;re warm.<\/p>\n<p>20:04:43 <b>ianbruene<\/b> | also incredibly fragile<\/p>\n<p>20:05:20 <b>esr<\/b> | I guess you&#8217;re close enough.<\/p>\n<p>20:05:36 <b>esr<\/b> | Reason #2: IT&#8217;S *SIMPLE*.<\/p>\n<p>20:06:17 <b>esr<\/b> | Not a consideration to be despised.  Smart programmers do as little work as possible on unimportant things, so they can save brainpower and effort for important things.<\/p>\n<p>20:06:43  <b>ianbruene<\/b> | am I right in thinking that this is considered ok because 1. very early: the field hadn&#8217;t really developed yet<\/p>\n<p>20:07:07  <b>ianbruene<\/b> | and 2. no need for interop, or devastating if data lost<\/p>\n<p>20:07:21 <b>ianbruene<\/b> | unlike a certain *cough* MS Word *cough*<\/p>\n<p>20:07:32 <b>esr<\/b> | Pretty much correct.<\/p>\n<p>20:07:45 <b>esr<\/b> | However, note 2 subtle points:<\/p>\n<p>20:08:09 <b>ianbruene<\/b> | ok, *readjusts utility function for memdump fileformat tradeoff*<\/p>\n<p>20:08:20 <b>esr<\/b> | 1. Format is rather less fragile than you think (I&#8217;ll explain that).<\/p>\n<p>20:08:33  <b>ianbruene<\/b> | I only knew of the ludicrous example of the MS formats previously<\/p>\n<p>20:09:27 <b>esr<\/b> | 2. The FORTRAN save\/restore code was really nasty and complicated.  It doesn&#8217;t get as simple as it now is unless you have fread\/fwrite and a language with structs.<\/p>\n<p>20:10:07 <b>esr<\/b> | Now, why this isn&#8217;t as bad a choice as you think:<\/p>\n<p>20:10:31 <b>ianbruene<\/b> | (pre-guess: everything is pre-swizzled)<\/p>\n<p>20:11:56 <b>esr<\/b> | No. It&#8217;s because any processor this is likely to run on uses the same set of struct-member aligment rules, what&#8217;s called &#8220;self-aligned&#8221; members.  So padding won&#8217;t bite you, just endianness and word size.<\/p>\n<p>20:12:54 <b>ianbruene<\/b> | *blink* oh&#8230;. another win from the intervening steamroller of standardization<\/p>\n<p>20:13:17 <b>esr<\/b> | Precisely, another steamroller win.<\/p>\n<p>(Editor&#8217;s note: ianbruene is aware that at the time the original ADVENT was written, greater diversity in processor architectures meant that the structure member alignment rules were more variable and more difficult to predict. So you took a harder portability hit from using a structure dump as a save format then.)<\/p>\n<p>20:12:19 <b>esr<\/b> | Wait, I want to give you a pointer<\/p>\n<p>20:12:58 <b>esr<\/b> | Read this, if you haven&#8217;t: http:\/\/www.catb.org\/esr\/structure-packing\/<\/p>\n<p>20:13:26 <b>ianbruene<\/b> | read it before, been quite a while (and didn&#8217;t have anything to put it into practice)<\/p>\n<p>20:14:22 <b>esr<\/b> | So, reread with this save format in mind.  Go through the reasoning to satisfy yourself about what I just claimed.<\/p>\n<p>20:14:32 <b>esr<\/b> | It won&#8217;t take long.<\/p>\n<p>20:16:06 <b>esr<\/b> | Now, this does *not* mean a memory dump would be a good format for anything much more complex than this game state.  We&#8217;re sort of just below the max-complexity threshold here.<\/p>\n<p>20:16:44 <b>esr<\/b> | And we do get screwed by endianness and word-size differences.<\/p>\n<p>20:17:39 <b>esr<\/b> | But&#8230;let&#8217;s get real, how often are these save files going to move between machines?  This is not data with a long service lifetime!<\/p>\n<p>20:19:49 <b>esr<\/b> | OK. Continuation of exercise:<\/p>\n<p>20:20:28 <b>esr<\/b> | What&#8217;s the simplest way you can think of to design an eyeballable save format?<\/p>\n<p>20:21:14  <b>ianbruene<\/b> | *thinks* (given that I know nothing of the internal structure of advent)<\/p>\n<p>20:22:45 <b>esr<\/b> | You don&#8217;t need to.  Look at the structure definition.<\/p>\n<p>20:23:47 * | <b>ianbruene<\/b> looking up struct<\/p>\n<p>20:28:34  <b>ianbruene<\/b> | well *one* simple way of doing it would be to do a (I forget what the format is called) var=value\\n format, with the save function being a giant printf of doom and the load function being a giant switch of doom.<\/p>\n<p>20:28:44 <b>ianbruene<\/b> | I don&#8217;t think that is *the* simple one though<\/p>\n<p>20:30:06 <b>esr<\/b> | That sort of thing is generally called a &#8220;keyword\/value&#8221; format.  It is the most obvious choice here.  Can you think of a simpler one?<\/p>\n<p>20:30:42 <b>esr<\/b> | (I&#8217;m not sure I can.)<\/p>\n<p>20:31:16  <b>ianbruene<\/b> | ok, we know the struct &#8220;shape&#8221;, could arrange for all of type X, all of type Y, etc. to be in contiguous spans.  Sequence either hardcoded or using a build time generator for the var names. Hmmmmmmmm&#8230;.. while it has a certain elegance it seems brittle and complex<\/p>\n<p>20:31:25 <b>esr<\/b> | Yes. It would be that.<\/p>\n<p>20:31:38 <b>esr<\/b> | Pretty classic example of &#8220;too clever by half&#8221;, there.<\/p>\n<p>20:33:35 <b>ianbruene<\/b> | ok, ignore assuming a shape, it would be *possible* for a code generator to simply look at the struct and create a pair of load\/save functions from it, using either the internal names, or special comments in the definition<\/p>\n<p>20:33:59 <b>ianbruene<\/b> | I don&#8217;t think the format itself can get any simpler than key=value though<\/p>\n<p>20:34:12 <b>ianbruene<\/b> | there isn&#8217;t much complex structure in the save.<\/p>\n<p>20:34:21 <b>esr<\/b> | I think you&#8217;re right.<\/p>\n<p>20:34:33 <b>ianbruene<\/b> | it isn&#8217;t a bunch of logical blocks of different rooms and characters<\/p>\n<p>20:35:09 <b>ianbruene<\/b> | if there were there would be useful tradeoffs in how you grouped things<\/p>\n<p>20:35:45 <b>esr<\/b> | Good!  That was a sound insight, there.<\/p>\n<p>20:34:59 <b>esr<\/b> | Now, you&#8217;ve correctly described a way to implement dump as a giant printf.<\/p>\n<p>20:35:27 <b>esr<\/b> | Do you as yet know enough C to sketch restore?<\/p>\n<p>20:35:39 <b>ianbruene<\/b> | *thinks*<\/p>\n<p>20:36:24 <b>ianbruene<\/b> | ok, the template I&#8217;m thinking is similar to the packet handling code for mode 6 (python side). but it is more complicated due to C<\/p>\n<p>20:37:29 <b>ianbruene<\/b> | read until get a token, slice off the token, feed the token into the Switch of Doom<\/p>\n<p>20:37:47 <b>ianbruene<\/b> | the SoD sets any vars it gets tokens for<\/p>\n<p>20:38:07 <b>ianbruene<\/b> | if the file is well formed you get all the data you need<\/p>\n<p>20:39:49 <b>esr<\/b> | Alas, you&#8217;ll find actually doing restore in C is a PITA for a couple reasons.  One is that you can&#8217;t switch on a string&#8217;s content, only its start address &#8211; C switch only accepts scalars.<\/p>\n<p>20:40:31 <b>ianbruene<\/b> | grrrr, so you have a big ugly set of str compares in if statements<\/p>\n<p>20:40:37 <b>esr<\/b> | Indeed, you&#8217;re going to write a big fscking if () with a whole bunch of strcmp() guards.<\/p>\n<p>(Editor&#8217;s note: There&#8217;s another way to do it, driven from iterating through a table of struct initializers, that would be slightly more elegant but no simpler.)<\/p>\n<p>20:40:38 <b>ianbruene<\/b> | I forgot about that<\/p>\n<p>20:41:29 <b>ianbruene<\/b> | this is something where your code style becomes *very* important or it will be an ugly, incomprehensible mess<\/p>\n<p>20:41:45 <b>esr<\/b> | Yes. Now you begin to see why I went to stupid fread\/fwrite and stayed there.<\/p>\n<p>20:42:31  <b>ianbruene<\/b> | and the obvious way to do it in something like python (magic introspection to class elements or dict keys) doesn&#8217;t work here<\/p>\n<p>20:43:03 <b>esr<\/b> | Right.  Replacing this binary dump with something clean and textual is not a terrible idea, but really only justifies itself as a finger exercise for a trainee, like playing scales to learn an instrument.<\/p>\n<p>20:43:17  <b>ianbruene<\/b> | I see<\/p>\n<p>20:43:39  <b>ianbruene<\/b> | hence why you mentioned in the blog that it was very low priority<\/p>\n<p>20:43:46 <b>esr<\/b> | Right. The absence of introspection is the other lack in C that makes it a PITA.<\/p>\n<p>20:44:17 <b>esr<\/b> | And you&#8217;ve extracted most of the value of the finger exercise by thinking through the design issues.<\/p>\n<p>20:45:02  <b>ianbruene<\/b> | when you get to do it introspection is a gigantic win, makes it difficult to remember how bad it is when you don&#8217;t get it<\/p>\n<p>20:45:08 <b>esr<\/b> | Yes.<\/p>\n<p>20:46:05 <b>esr<\/b> | Those of us who started in LISP learned this early. Took forty years for the rest of the world to catch up, and they&#8217;re only getting there now.<\/p>\n<p>20:46:40  <b>ianbruene<\/b> | I have done only the barest toying with lisp, barely even hello world level<\/p>\n<p>20:47:00  <b>ianbruene<\/b> | but even that (coupled with some reading of a lisp book) changed the way I thought<\/p>\n<p>20:47:35  <b>ianbruene<\/b> | plenty of times I&#8217;ve hit a snag of &#8220;this would be *so much easier* if I could do a lisp macro in python&#8221;<\/p>\n<p>20:47:52 <b>esr<\/b> | Indeed.<\/p>\n<p>20:48:33  <b>ianbruene<\/b> | incidentally, has GvR used lisp at all? the impression I&#8217;ve heard is that he doesn&#8217;t like the lispy features?<\/p>\n<p>20:49:11 <b>esr<\/b> | He doesn&#8217;t. Back in the late &#8217;90s I practically had to arm-wrestle him into not killing lambdas.<\/p>\n<p>20:49:45  <b>ianbruene<\/b> | [insert rant against anyone who thinks lambdas are useless]<\/p>\n<p>20:51:37 <b>esr<\/b> | I think I might edit this dialog into a blog post. Start it with the Heinlein quote about the ideal university: a log with a teacher on one end and a student on the other.<\/p>\n<p>20:51:51 * | <b>ianbruene<\/b> grins<\/p>\n<p>The foregoing was transcribed from IRC and lightly edited to fix typos, fill out sentence fragments, and complete 80%-articulated ideas we mutually glarked from context.  A few exchanges have been slightly reordered.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The craft of programming is not a thing easily taught. It&#8217;s not so much that the low level details like language syntaxes are difficult to convey, it&#8217;s more that (as I&#8217;ve written before) &#8220;the way of the hacker is a posture of mind&#8221;. The posture of mind is more essential than the details. I only&hellip; <a class=\"more-link\" href=\"http:\/\/esr.ibiblio.org\/?p=7616\">Continue reading <span class=\"screen-reader-text\">A teaching story<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23,13],"tags":[],"class_list":["post-7616","post","type-post","status-publish","format-standard","hentry","category-hacker-culture","category-software","entry"],"_links":{"self":[{"href":"http:\/\/esr.ibiblio.org\/index.php?rest_route=\/wp\/v2\/posts\/7616","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/esr.ibiblio.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/esr.ibiblio.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/esr.ibiblio.org\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/esr.ibiblio.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=7616"}],"version-history":[{"count":10,"href":"http:\/\/esr.ibiblio.org\/index.php?rest_route=\/wp\/v2\/posts\/7616\/revisions"}],"predecessor-version":[{"id":7627,"href":"http:\/\/esr.ibiblio.org\/index.php?rest_route=\/wp\/v2\/posts\/7616\/revisions\/7627"}],"wp:attachment":[{"href":"http:\/\/esr.ibiblio.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=7616"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/esr.ibiblio.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=7616"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/esr.ibiblio.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=7616"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}