Programming Languages, Part C
The last course in the Programming Languages course trilogy. I am going to miss learning from Dan Grossman. Programming Languages A, B, C were some of the most instructive, satisfying, and fun courses I've ever taken. Here's to hoping (probably naively) that the rest of the OSSU curriculum is this awesome.
My progress
I went on vacation in the middle of taking this course, so it took me about 2 weeks longer to complete than I initially estimated. I didn't lose all my momentum, but even after just a 10 day break I had to put in some real effort to get back up to speed before I could make forward progress again. Another reminder that consistency is incredibly important.
My experience
As good as this course was, I still came out with a much higher view of functional programming than object oriented programming. Now that it's been a few weeks and I've had time to digest eveything, I have come to adopt the "right tool for the job" mentality, but OOP just did not land with me like functional programming did.
One of the assignments was to port an interpreter written in SML to Ruby. Over and over I found myself thinking that the Ruby version of the program was so much more complex and involved so much more ceremony than the SML version. I felt like I was spending a lot of my time in Ruby setting up to do the thing I wanted to do, instead of just doing the thing. It was also harder for me to follow the path my code would take, which consequently made debugging my Ruby code harder and cost more time than in SML. This echos my (limited) experience in other heavily OOP languages like C# (though debugging was a little easier in C# because of the tooling and it being staticly typed). However, I did feel like a wizard after nailing the double-dispatch portions of the porting assignment on my first attempt.
I also developed a strong preference for static typing over dynamic typing. There were a number of times I discovered a bug in my Ruby code after way too much debugging, only to realize that the problem was a minor typo or some other simple mistake that caused the program to misbehave in some weird way. The perfect example of this came from another student's solution while I was peer reviewing it (warning: this is a bit technical):
There was a method that should never return false, but was returning false in certain situations. The problem was here:
The issue is in that one-line ternary expression. Evertything before the ? should be wrapped in parenthesis, otherwise Ruby will evaluate the and of everything before the and keyword, and everything to the right of it. In other words, with the way this code is written, the and expression is the outermost operation, not the ternary expression - which will always return a Boolean value. The correct version of this code is this (notice the and expression wrapped in parenthesis):
(inbetween(@x, seg.x1, seg.x2) and inbetween(@y, seg.y1, seg.y2)) ? Point.new(@x, @y) : NoPoints.new
To top it off, this was buried beneath a few layers of double-dispatch shenanigans. It took me about an hour to figure this out, and who knows how much time was spent by the student who originally wrote the code - this didn't pass the given tests so I'm guessing the student just gave up trying to fix it and turned it in. Static typing would have caught this before runtime and would have given a helpful error message.
Next up
I decided to take a small break from Core Programming and start on Core Math. I have fond memories of learning Calculus in high school and I am much more excited to revisit that than learn more about object-oriented programming. I could feel some resistence toward toward the thought of learning "Object Oriented Design" with Java, so I figure this was the better move to avoid burnout and preserve consistency.