inky has a blog inky writes about stuff

January 3, 2016

Advent of Code pt 2

Filed under: Uncategorized — Tags: , , — inky @ 10:26 pm

More on Advent of Code. This next set of problems was a little trickier than the previous ones, but still not too scary. I plunged into the deep end for the language selection, though.

Day 10
Language: Javascript (under Nashorn)
Prior experience: Some
Development time: <1 hour

Javascript, of course, has issues with taking input from the user, so when I saw another question that didn’t have a separate page for input, I grabbed it. I thought I had made things a little harder for myself here by an earlier decision that everything had to run at the command line on my linux box, which meant I couldn’t actually run the javascript stuff in a browser. It turns out the oracle java install ships with jjs, though, so it wasn’t as bad as I was expecting. No big deal on the problem, except having to bump the java heap size to calculate part 2. I suspect there might actually be a way to stream the answer through such that it never holds the whole string in memory at once, but it wasn’t necessary here so I never bothered.

Day 11
Language: Javascript (under Node.js)
Prior experience: A little with gulp and grunt (wrt Node; more for javascript in general)
Development time: <1 hour

Kind of weaksauce to do two entries in javascript, but I was in a hurry and trying to catch up (I think I did entries 1-6 on day 5 (6 after midnight eastern), 7-9 on their actual days, and then 10 and 11 on day 14, and it just got worse after that. Which is why I’m posting this now when the thing ended on the 25th!). I did legit use some node modules, and it uses node’s argv access, but still, javascript is pretty much javascript. In terms of the problem, note that the particular letters restricted (i, o, l) and other rules are given in the problem statement, so I didn’t feel obliged to take them in as input or anything. Obviously it’s good style to make the code modular enough to easily add more rules, but it’s fine to hard code what the actual rules are. And that’s how programming goes in general – it’s not that any part of a program is immutable, it’s that it requires varying amounts of effort to modify different parts of the code. A well-written program is one where the parts you want to change often are easy to change (and the corollary to this that folks have pointed out is well-written programs all eventually become badly-written, because they change until they are changed into a form that is no longer easy to change). Sometimes easy to change means they’re in an external config file or taken in on the command line, but sometimes it just means the code is designed in a way to make it easy to change it. Here, you could take in the banned letters for the banned letter rule from the command line, but how would you capture the other rules with command-line arguments? If you can’t make most or all of the rules configurable, it’s probably better not to make any of them configurable, if you don’t have some insight about which ones are going to be changing the most frequently.

Day 12
Language: jq
Prior experience: Some
Development time: 1-2 hours

If you don’t know what jq is, the webpage says it’s like sed for JSON, which seems like dialog from a scene in a book to show the wacky way people in the future talk. Anyway, jq is cool but mostly the thing I use it for is jq ‘.’, which pretty-prints the input. This problem looked like a good excuse to learn more (jq wasn’t on my language list initially, but I thought of it when I saw the problem was in JSON), so I picked it up and did the first part in fifteen minutes fairly elegantly. Then I tried the second part, and that took an hour and fifteen minutes and was clumsy. I like the concept of jq, but in practice I’m not sure learning the language is better than what I was doing before, which is just jq ‘.’ and piping to grep to use existing unix tools (yes, people in the future talk funny).

Day 13
Language: Scala
Prior experience: None
Development time: 1-2 hours

My impression of scala is that it’s in the same bucket as clojure, the “use the jvm and get all the libraries and portability that implies, while drawing on functional languages for some cool tricks” bit of the venn diagram. Clojure goes ahead and tries to just be a scheme dialect, while scala sticks closer to java syntax-wise but not so close that java experience is much help. I don’t know if it actually looks like anything else in particular, or if they just ad-libbed syntax to fit in the features they need. Overall the syntax is fine, but I’m deeply resentful that they went with parentheses for array indexing instead of square brackets. It’s funny what bits of syntax tweak me – like, I’m also not a fan of “(0 until 10)” to denote a range, but 0..10 would be fine, and “line match { .. }” is also a little not-great. I think what it comes down to is I’m not big on using words for infix operators – I like language keywords to come at the start of the line so I can know what to expect. These problems are also giving a pretty extensive survey of different languages’ facilities for reading in lines from a file. I give scala a mixed rating in this department – I like the general Source thing and being able to just do getLines, but having to explicitly do the try/finally to close the Source is pretty old-school for a theoretically modern language. Anyway, overall I didn’t think scala looked good enough to lure me off java, especially with the new java 8 functional stuff, but in the alternate universe where I was using scala instead of java I think I’d be pretty content with it.

Day 14
Language: Erlang
Prior experience: A little
Development time: 4-6 hours

First off I would like to complain about the problem. For part 1, where you’re trying to figure out which reindeer got the farthest given that they’re alternating between flying and resting, there are two obvious ways to calculate it. One is you simulate each second and see what they’re doing at that point and adjust the distance counter appropriately, and the other is you say “ok, they went 1000 seconds, that’s five full cycles where they went 250 meters each and one partial where they only went 103 meters, so their total distance is 5*250 + 103”. The second one is simpler to code and more performant, and it also turns out to be totally useless in solving part 2 of the problem.

I don’t think it’s discussed anywhere what the purpose of having a hidden part 2 is of each problem, and really, I’m not sure the author has a clear idea either, since the kinds of part 2s vary. Sometimes the part 2 is “ok, do the part 1 thing again, only moreso” (like day 4), sometimes it’s “do a variant of the work you did for part 1” (like day 2), and sometimes it’s something totally different (like the notorious day 19). It’s also interesting that the author never changes the input for part 2 – you can imagine lots of possible part 2s for these if you’re allowed to say “now use this new input file”. Personally, I’ve taken the existence of part 2 as an additional implicit requirement – it has to be correct, performant enough to get the answer before I get tired of waiting, and flexible enough to handle some unnamed requirement change. Which is totally reasonable given real-world software design, but at the same time, it seems a little unsporting if the change is too large. Like, for this one, I would have preferred a part 2 like “after resting, every reindeer now stops to eat for ten turns; what’s the farthest distance after X seconds now?” But maybe I’m just cranky because I didn’t make my code flexible in the right way the first time.

Ok, with that out of the way, let’s talk about erlang. Erlang isn’t exactly the new hotness, given that it’s from the 80s, but it’s getting more buzz these days as its area of specialty (distributed systems) gets more and more popular. My main interaction with it is that RabbitMQ is written in erlang, and I had to write a plugin for RabbitMQ, so I wrote some erlang. Then I wrote some more for this problem.

If you look at, say, old computer games, what you see is that in the early phases, there’s a lot of crazy stuff as people play around with the new medium (ie, semantics) and figure out how to have the user interact with the game (ie, syntax). As time goes on, conventions get established for how the interface should work, and to a lesser extent what the content should be like; people still do some new things but it’s much less accidental innovation and more “ok, let’s see what happens if we violate this genre convention”. Anyway, so using erlang is like playing one of those early games that was a failed evolutionary branch. Commas instead of semicolons to terminate lines? Variables all have to be capitalized, and lower-case letters denote symbols? Namespace paths delimited with colons instead of periods? None of these are objectively bad decisions, but they make using it kind of sucky if you’re used to current languages. It’s not like development on erlang has stagnated – I used a perfectly-serviceable modern regexp package somebody added to the language, and the escript stuff is a great workaround for it otherwise being a massive pain to run erlang scripts from the command line – but you can’t fix fundamental syntax issues, you can just fill in library gaps (for example, there are structs with named fields but the syntax is embarrassing – it’s clearly super hacked in, worse than java generics). Also, the debian package to install erlang is just terrible – it has like 40 separate subpackages and takes forever to install.

Now, if I were smart, I would also have done something with elixir (a modern python/ruby-syntax language that runs on the erlang VM, sort of a clojure to erlang’s java) so I could compare how that feels, but I didn’t. Oh well! Maybe next year.

Day 15
Language: Haskell
Prior experience: None
Development time: 10-12 hours

Haskell! Warrior of legend! Scion of academia! Prince of monads! Served by purity-sworn functional programmers!

Haskell and Rust were the two languages I was expecting the most challenge from in this entire game, and Rust I’ve tried to do something in earlier (and failed). Haskell I didn’t know anything about except by repute. I’m still not sure I know much coherent about Haskell, but I do have some scattered thoughts.

With most languages, even ones I’ve never used before, I’m a competent enough general programmer enough to write all the code out with help from various web pages, then compile, fix bugs, recompile, etc, until it works. I tried that here and it blew up bigtime; I had so many errors I couldn’t get the code into shape enough to figure out what was causing the errors. So I had to go back to the programmer 101 thing and build things out function by function, getting it more or less solid before moving onto the next bit. Looking back on it, I think Haskell has to bear some of the blame here – the language encourages you to chain multiple function calls together (like foo(bar(baz))) in other languages’ syntax), but this can get really hairy to parse without parentheses, enough so that they have the $ operator which, as far as I can tell, is there purely as duct tape to fix precedence issues. I’m also not thrilled how Haskell doesn’t have the courage of its convictions for operator ordering, and it makes symbols infix and letters prefix, which c’mon.

Ideally writing some Haskell would have taught me an important lesson about monads. I originally didn’t think it did, but I guess there was one thing. For virtually all these problems, I wrote code that was like “read stuff from a file into a list in memory, then do some calculations on the list, then get a result and print it out”. I tried to do that in this one and got lost in a maze of twisty monads all alike. To get it to work, what I ended up having to do was “read stuff from a file and call this callback on the read stuff – that callback does calculations, then gets a result and prints it out”. Looking it over, I think this is exactly a monad, but I couldn’t swear to it. And I certainly don’t understand the deal with the <- operator.

Day 16
Language: Kotlin
Prior experience: None
Development time: 1-4 hours (records are a little unclear)

Kotlin is basically what you get when java development stagnates. People get tired of waiting for java to add important core features, so they decide to hack those features in themselves, and then since they’re writing a new language anyway, why not throw in some other crazy features. Like, kotlin adds non-sucky anonymous functions, which is pretty clearly a good feature. But then it also adds that if the anonymous function only takes one argument, you don’t have to do { x -> x*2 }, you can just do { it * 2 } (that is, if you don’t specify any arguments, it’s assumed to be a one-argument function with the argument named “it”). This is crazy! There is no way you’d see something like this in java! It also turns out to be really convenient. I guess that was how kotlin felt to me in general – like java but slightly nicer. Moreso, I think, than scala – scala feels like it’s java with some plusses and a few minuses, and kotlin feels like it’s all plusses. But it also feels like the amount of possible upside it has is limited, and it’s going to decrease over time as java gets developed. Like, the value prop of kotlin is “java with a few things fixed”. Which means it can’t look too different from java or it’ll scare off the java people. But if kotlin doesn’t look that different from java*, it can’t offer too many advantages over java, and it’s easier for java to close the feature gap – something like clojure, which is more radically different, can have more advantages over java but it’s also going to have a steeper learning curve and smaller audience.

*That said, java was designed to look like C++ so as to not scare off too many people, and it’s really pretty different, seems like.

Unlike day 14, I was able to correctly anticipate what part 2 would be when I was writing part 1, so I had a minimum of changes necessary. That was nice.

Day 17
Language: Groovy
Prior experience: Some tweaking of gradle build files
Development time: <1 hour

It’s a little hard to take groovy seriously somehow. I mean, I don’t have a good reason why it didn’t take off – maybe it’s a cultural thing, like java people don’t overlap much with scripting language people so they didn’t see the advantage of using the java libraries, or maybe the java libraries genuinely don’t add much value to the breadth of existing python/perl/ruby/node libraries out there. But yeah, groovy feels like an attempt to reboot java as a happenin’ teen because that is what the kids are into these days. I also see groovy has the same “it” thing in anonymous functions that kotlin does – I guess kotlin got it from groovy and not the other way around, or maybe there’s some common ancestor.

Next time, I’ll do the final set of problems and gripe extensively about problem 19.

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress