1. 9

What are you doing this weekend? Feel free to share!

Keep in mind it’s OK to do nothing at all, too.


  2. 9

    Starting my game for Lisp Game Jam 2019! I finally feel comfortable enough in Fennel and LÖVE to fully participate.

    Other than that, helping my new roommate move in—I’ll have to say goodbye to using my spare bedroom as an office, but that’s a small price to pay for the benefits of having a good roommate!

    1. 5

      Revision is on, so I will sure enough enjoy another round of astonishing pieces of modern art. It is truly amazing what people rip off on old hardware.

      Competitions include live shader coding, 4K demos, 8-bit soundtracks and much more - something for everyone. I suggest hitting the live stream or searching for recordings on the web;

      1. 5

        BMX racing - although due to injury I will be marshalling rather than racing this weekend…

        1. 5
          • Sailing races with my crew, our only aim is to not be last in all races! Easter Sunday in the UK though, so it’ll be interesting to see who makes it down.
          • Tasting a new beer from a local brewery, organised by the local CAMRA group
          • As the weather is supposed to be amazing (it’s a long weekend in the UK, good weather is not a thing we were expecting) I’ll hopefully break the bike out and go cycling.
          1. 3

            Don’t be last / beer can sailing races are the most fun :) The competitive people get to be competitive, the people who just want sail a boat fast can, and lots of fun is (hopefully) had by all. Do y’all get together for beers and food at the end?

            1. 3

              We generally hang around and have a cuppa together afterwards yeah. It’s not super competitive, and my first proper season of racing so not being last feels like an achievable goal, but still something I have to work at. (I raced twice last season, and accidentally came third in the class at the club overall for the year. Statistics are a funny thing.)

          2. 5

            Make some more progress on my dactyl manuform hand-wired keyboard. Make progress on some software project documentation planning.

            1. 3

              Woohoo, cool! Could I ask you to please PM me with your thoughts after you try using it for the first time? I’m kinda considering maybe making it too, but it’s a big money investment for me, and I’ve heard some mixed opinions, so it’s hard for me to decide :( And I don’t have access to a real specimen to try for myself beforehand…

              1. 3

                Yeah, I’ll do that. :)

            2. 4

              Seeing exhibitions in London and participating in Extinction Rebellion blockades.

              1. 4

                Working on a library for doing deep copying in Go. We don’t need to do it often at $work, but when we do, it’s super inconvenient and error prone to write it out by hand. (Or, if that’s too annoying, we have hacks that just gob encode/decode to get a deep copy, when it works.)

                The best library out there for this, as far as I can see, is copystructure. But it’s not particularly configurable and doesn’t help with unexported fields.

                1. 1

                  I don’t program in Go. Can you please explain what an unexported field is. Also, just curious what problem space has you deep-copying data?

                  1. 2

                    It doesn’t really have anything to do with the problem space. You just occasionally want to be able to copy stuff, for example, when you want to ensure nothing shares any memory. Kubernetes has its own bespoke code generation tooling for this, and HashiCorp uses copystructure in a lot of their stuff AIUI.

                    But yeah, unexported fields just refer to the visibility of members in a struct. One can only access unexported fields within the same package in which the struct is defined. Reflection based tools normally respect this, but deep copying is often orthogonal to the visibility of struct fields, particularly since you might want to deep copy something that has unexported fields. Since copystructure doesn’t support this, you wind up having to annoying role your own manual and error prone deep copy implementation for structs with unexported fields, or you wind up just exporting stuff so that it plays nice, which obviously isn’t ideal.

                    1. 1

                      I wish there was an interface and/or de facto standard for this. Then you can just implement what it means for a thing to copy itself at that thing’s level, and anything wrapping that can just internally call the “Copy” of what they wrap.

                      1. 1

                        It’s tricky to just use interfaces to solve this problem. The first issue is that the natural type signature of a copy method is something like this:

                        func (t T) Copy() T

                        But you can’t express that in Go in an interface, so you wind up with this:

                        type Copier interface {
                            Copy() interface{}

                        … which is obviously less than ideal, and makes it a bit annoying to use.

                        Second issue is that even if you accept the interface{} here, it’s still a major pain to actually write out the implementation of it. It’s a place in the code that’s just waiting for hard to diagnose bugs and it’s hard to unit test effectively. The simple case where this falls over is when you add a new field to a struct. There’s nothing that will actually tell you that the new field also needs to be added to the Copy implementation for that type. You’ll only find out about it when some other piece of code uses the Copy method and a subtle bug crops up because your copy doesn’t include everything.

                        You can devise unit tests that will fail, but you need another piece of machinery. That is, you need something that says, “fail this test if this value has any zero values in it.” That way, you can write a test that asserts your Copy routine roundtrips correctly and will also fail if you add a new field to a struct but forget to update both the test and the Copy implementation.

                        My idea here is to take a page out of go-cmp’s book and look for methods with the type signature, func (t T) Copy() T and use that before falling back on to automatic reflection based machinery to do the copy. That way, types can precisely control how they are copied when necessary, but otherwise, a Copy will “just work” in the common case.

                      2. 1

                        Cool thanks. Ah it sounds like the problem space is distributed computing, or short lived tasks or containers in this case, which Go is suited for.

                        I do alot of web development and outside of maybe cloning an online Order or some basic strings I don’t do this much.

                        Recently the only copying I’ve done programmatically is to “clone” a tables from db server 1 to server 2 as apart of an adhoc task. Interestingly enough usually I do this via insert into select from statements, but recently learned of Federated tables in Mysql which make the cloning even task between distributed mysql instances easier. Again, in this case as you’ve noted sometimes cloning has shortfalls. In Mysql a Federated table has various limitations such as limited allowed DDL statements on the table, or certain password character limitations.

                      3. 1

                        An unexported field is basically a private field in a struct. Unexported fields are only visible within the package they are defined in.

                        1. 1


                      4. 1

                        Oh, cool, so you’re still writing Go! Good to know. I somehow though you’re a Rust-only celebrity now ;D Good luck regardless, anywhere the Road takes you! :)

                        1. 1

                          Yeah I never stopped. I just don’t typically do it in my free time any more.

                          1. 1

                            Given your experience in both, I admit I’m now quite curious what are your thoughts on pros & cons of each? But no pressure or whatever; suppose people often ask you about this ;)

                            1. 13

                              That’s kind of a loaded question. :-) People don’t actually often ask me that question. A real answer is probably pretty involved. Personally, I think the most important benefit of Go is its “simplicity.”[1] And by that, I mean that it is very easy for people to hit the ground running with Go without too much fuss at all. There are a limited number of constructs available in the language, so it’s in general pretty rare to stumble into something that’s difficult to understand because of its abstraction. (My own experience supports the pithy phrase, “abstraction solves all problems, except for the problem of abstraction.”) If code is hard to understand, it is usually, at minimum, concrete, such that it doesn’t require thinking carefully through sophisticated type system shenanigans. The only truly complex Go code I run into is either deeply reflection based (which is pretty rare outside highly reusable shared libraries) or a mess of goroutines using a non-standard concurrency pattern. But I’ll take that over the pthreads code I’ve seen in C any day.

                              Of course, like any good trade off, its strength is also its weakness. When you bump into Go’s abstraction limits, it can sometimes be really annoying. For example, I often really want a generic Option type that encodes the possibility of absence—even if it’s only enforced at runtime—since it isn’t always the case that the zero value is a useful indicator of absence. e.g., Dave Cheney’s famous “functional options” article uses the fact that the default value of 0 can also have meaning in a particular domain, so it isn’t sufficient to treat 0 as “missing” (and thereby resort to some other default). This in turn serves as part of a motivation for avoiding configuration structs entirely. Of course, you wind up with more machinery, but this can be worth it for a highly reusable library.

                              Rust also has abstraction limits, but I very very rarely run into them in the work I do. Of course, those limits are much much higher than Go, and as a result, using the language is itself much more complex. I personally think it’s still comfortably less complex than using Haskell, and definitely less complex than using C++, but it’s pretty clear that it takes a lot more ramp up time to get started with Rust than it does Go. This has not only been echoed by many other people that have tried Rust (“I gave up a couple times before succeeding, and now writing Rust is not that hard” is a common story), but is also consistent with my own personal experience teaching both Go and Rust. I’ve helped people learn both, and the number of times I needed to go to a whiteboard and carefully explain a somewhat dense snippet of code is markedly different between the languages.

                              Obviously, this trade off is intentional. Rust isn’t complex for no good reason. There are super good reasons for almost all of its complexity, and they generally boil down to some combination of safety and performance. Go also values safety and performance, but not nearly to the degree that Rust does. In Rust, I can push a lot more invariants into the type system quite naturally and idiomatically, where as with Go, the language actively fights against encapsulation (in the “data hiding” sense) in a lot of circumstances. Again, this comes down to complexity. For example, it’s totally reasonable to want to write your own type that exposes a map-like interface—and maybe uses a map internally or maybe not—but this is just fundamentally impossible because the language blesses the map type with special syntax. You can see this clearly even when the standard library struggles with it, for example, the sync.Map type and its Range method. Moreover, if you’re hiding things, this completely destroys the utility of most of the ecosystem’s reflection based encoding/decoding infrastructure. You wind up having to write error-prone serialization goop if you want to hide your internals. (Error-prone because if the internals change, e.g., with an additional field, then you have to remember to update the serialization goop. It’s possible to write unit tests for this, and we have them at work, but that in turn requires knowing to add the field to the test to serialize in the first place. So we have test helpers for that which can check that all values in a particular type do NOT have the zero value. So if you add a new field, that test automatically fails.) So… encapsulation in Go is possible, you just wind up getting punished for it, so sometimes you invariably choose “encapsulation isn’t worth it” just because of how the language is designed, instead of what makes sense for you specific circumstances. The frequency with which this happens is annoying in my experience. In Rust, I almost never have to make this sacrifice; encapsulation is well supported and nearly effortless compared to Go. (I could articulate some cases where Rust punishes you too, but that gets into the weeds and they are fairly rare.)

                              [1] - I purposely use the word “simple” in quotes, because people love to imbue their own definition of what “simplicity” means. e.g., You’ll see plenty of people arguing that Haskell is actually “simpler” than Go just by comparing their core language specifications. e.g., The full generality of Haskell makes it “simple.” I don’t think this perspective is 100% wrong, but it just completely misses the very obvious point I’m trying to make and completely derails the conversation into a brain-dead definitional turf war. That is, I think it’s completely uncontroversial to state that, as an observation, it is much easier, in general, to read, write, learn and productively use Go than it is Haskell. I don’t give two poops why that is (“it’s because we don’t teach functional programming as a first language” is a common refrain) and I don’t care if it isn’t true in some cases, because, obviously, individuals vary.

                              1. 8

                                This comment is far too good to be buried in a weekend update thread. :)

                                1. 2

                                  Thanks a lot for the response! And for your time taken to write it. Sure, I know it’s kinda loaded ;) but of all the people, who could be better posed to try and answer it in a balanced way than you! ;) As such, it carries a lot of value and authority to me.

                                  Personally, I’m currently kinda licking my scars after a first stroll into the Rust land. What surprised me most, is I think that it felt more complex than C++ to me; but I’m kinda coming to terms with the fact that it may just be because how far I got in C++, and how used to it I’ve become. Also, I was taken by surprise by the fact, that my difficulties weren’t with borrow checker per se, but rather secondary and tertiary consequences of BC on the typesystem coming at me from the least expected angles. With all that said, and with your report as an important point, I’m kinda starting to think I may just need to come back to trying a few more times in future, until I’ll maybe grok it at some point. But also my appreciation for simplicity and readability of Go is reinforced by your explanations.

                                  One more question, if I may: would you risk comparing the languages on a scale of “malleability” (ease of change), when some new features require deep refactoring of existing codebase? Do you find Go easier, Rust more supportive (b/c invariants in types), or do those kinda balance in your mind? You can answer by PM, or plain refuse to, if you’re afraid of being quoted on this in flame wars ;)

                                  1. 2

                                    Personally, I’m currently kinda licking my scars after a first stroll into the Rust land.

                                    I think that’s okay! I’m aware of a lot of people bouncing off Rust, at least initially. But I’ve heard a lot of success stories where people come back to it and figure it out.

                                    What surprised me most, is I think that it felt more complex than C++ to me; but I’m kinda coming to terms with the fact that it may just be because how far I got in C++, and how used to it I’ve become.

                                    Possibly. I am not a C++ practitioner, so it’s hard for me to say too much. I would hazard a guess that the presence of ubiquitous UB would bring its complexity above Rust almost on its own. In most Rust code that I write, I don’t need to think about UB at all, and I probably write more unsafe than most (since a lot of my work is in core libraries). With that said, I think this is a big rabbit hole, because C also has lots of UB, and I could see an argument to be made that C is simpler than Rust in the same sense that Go is simpler than Rust. It’s a pretty hand wavy thing in general, and I think “unknown unknowns” probably plays into this quite a bit. That is, I think a lot of people who write C or C++ are probably not intimately familiar with the language lawyering necessary to think carefully about whether some piece of code is actually UB or not, so the presence of UB might not contribute to the perceived complexity of the language.

                                    C++ also has a lot of other crud in it, and its template system is, I think, at least as complex as Rust’s parametric polymorphism facilities. But alas, we could circle the drain on this one forever. :-)

                                    One more question, if I may: would you risk comparing the languages on a scale of “malleability” (ease of change), when some new features require deep refactoring of existing codebase? Do you find Go easier, Rust more supportive (b/c invariants in types), or do those kinda balance in your mind? You can answer by PM, or plain refuse to, if you’re afraid of being quoted on this in flame wars ;)

                                    Hmm, good question. I haven’t thought about this one as carefully as I’ve thought about my encapsulation issues detailed in the previous comment. I think both languages ultimately make refactoring substantially easier than languages without a static type system, at least in my experience. I realize reasonable people might disagree on this, but it’s been reinforced for me personally many times throughout the years.

                                    In terms of Rust vs Go… I guess I honestly feel like refactoring is generally pleasant in both languages. I suppose, as you’ve guessed, I have found Go a bit harder to refactor in some cases, but this mostly ties back into my encapsulation argument in the previous comment. That is, sometimes I’ll just give up on encapsulation completely in Go for a particular type, and just expose its internals. Even if I think it’s the “wrong” thing to do all else being equal, you really just do not want to fight the language too much, so you have to pick your battles. In some cases, an exposed representation can make it awfully difficult to rigorously enforce an invariant. Even something as simple as imposing a “fail fast” change where you check an invariant on some piece of data on access is hard when everything is exposed, because you can’t control the access in the first place.

                                    Anyway, that’s all I’ve got for now. Please accept my thoughts with an appropriate grain of salt. I’m waving my hands a fair amount here. :-)

                                    1. 2

                                      Thanks. As to UB, sure, that’s what finally scared me away from C++, but I started finding out about it only once I was quite deep, unfortunately :( And I too fully believe much too many people still don’t grasp well enough what it means. In part it feels to me like many people don’t want to believe; I assume in part being subconsciously afraid of losing so many years of experience when switching tech stacks. Also, before Rust, there was no serious alternative. So, personally, I too hope Rust would eventually replace C++.

                        2. 3

                          I have one of the last major assessments in the initial training for my new job [1]. I’ll be away from Saturday morning until Friday doing some practical leadership stuff - the weather is set to be a cosy 25C, a massive improvement over the 3” of snow we had a couple weeks back on the practice!


                          1. 3

                            I think I’ll start reading through A Programmer’s Guide to Maths; I’ve also had this urge to play around with Smalltalk after reading The Dream Machine. Also, Easter brunch and trying to finish recovering a quick viral bug I caught the other day.

                            1. 3

                              Prepping for interviews. I really dislike whiteboarding. I think it’s borderline pointless. I guess it’s just how the game is.

                              1. 2

                                Amen and good luck.

                              2. 3
                                • working on hopefully finishing/polishing event registration site on volunteer time. Due around the 30th of this month. Everything seems to function ok but some areas need some final polish and robustness added.
                                1. 3

                                  Tonight I’m doing karaoke at Pub 85 in Kirkland, WA. Would be fun to meet up with any other Seattle-area Lobsters in person.

                                  1. 2

                                    Continuing to set up a self-hosted instance of sr.ht!

                                    I also pretty much wrapped up making the pretty printing library that I mentioned last time, so I’ll probably ask some friends to test it out and see if there’s anything else that would be good to have in there