We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Affordances in Programming Languages

00:00

Formal Metadata

Title
Affordances in Programming Languages
Title of Series
Number of Parts
65
Author
License
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language
Producer

Content Metadata

Subject Area
Genre
Abstract
A good design communicates the intended use of the object. In the physical world, this communication is accomplished by "affordances" as discussed by Donald Norman in "The Psychology of Everyday Things". Programming languages also have affordances, and they influence the kinds of solutions we develop. The more languages we know, the more we "expand our design space" so that we can come up with better solutions to the problems we face every day. We will look at a few example problems and show how they can be solved using the affordances provided by different languages.
39
SoftwareMultiplication signProcess (computing)Bus (computing)Magnetic stripe cardArrow of timeIdentity managementSlide ruleSinc functionComputer animation
Bus (computing)Magnetic stripe cardArrow of timeProgram slicingTouchscreen
Directed graphArrow of timeBitComputer animation
Integrated development environmentGroup actionPoint (geometry)TheoryVideo gameObject-oriented programmingGroup action1 (number)Crash (computing)PhysicalismBitMassOperator (mathematics)QuicksortMereologyComputer animation
SoftwareGoodness of fitMultiplication signPhysicalismFigurate numberSoftwareComputer wormElectronic mailing listUniform resource locatorProgramming languageWebsiteCartesian coordinate systemWeb 2.0User interfaceState of matterWeb applicationCoordinate systemForm (programming)Point (geometry)Social classDifferent (Kate Ryan album)Uniqueness quantificationSampling (statistics)RoutingComputer animation
Point (geometry)Chemical polarityCartesian productSocial classPoint (geometry)Slide ruleThetafunktionDistanceRadiusAngleCartesian coordinate systemTrigonometryWell-formed formulaComputer animationLecture/Conference
AngleWell-formed formulaPoint (geometry)MathematicsPolar coordinate systemSocial classEquations of motionThetafunktionDistanceLibrary (computing)View (database)Graph coloringCartesian productPolarization (waves)NumberMechanism design
Point (geometry)Instance (computer science)Greatest elementPoint (geometry)ThetafunktionCodeParameter (computer programming)Cartesian productPolarization (waves)Object-oriented programmingMessage passingIdentity managementVariable (mathematics)Social classConstructor (object-oriented programming)WordRow (database)Key (cryptography)Sign (mathematics)Computer animation
RadiusThetafunktionPoint (geometry)Constructor (object-oriented programming)RadiusCoordinate systemPoint (geometry)Constructor (object-oriented programming)ThetafunktionAngleObject-oriented programmingForm (programming)WritingProgramming languageSocial classRight angleComputer animationLecture/Conference
MathematicsSinePoint (geometry)Chemical polarityThetafunktionPolarization (waves)Social classConstructor (object-oriented programming)Point (geometry)Coordinate systemComputer animation
Exception handlingElement (mathematics)Symbol tableHash functionKey (cryptography)PRINCE2Block (periodic table)Element (mathematics)Different (Kate Ryan album)Form (programming)Condition numberMessage passingException handlingWordCodeSocial classBit rateCASE <Informatik>System callPoisson-KlammerPointer (computer programming)Variable (mathematics)Computer animationLecture/Conference
Block (periodic table)Block (periodic table)Message passingMultiplicationSocial classMultiplication signCondition numberComputer animation
Revision controlBlock (periodic table)Lambda calculusElement (mathematics)NumberCode2 (number)Matching (graph theory)Focus (optics)Parameter (computer programming)MathematicsWritingPattern languageComputer animationLecture/Conference
Software testingBlock (periodic table)HypothesisTheory of relativityData structureProgramming languageProcess (computing)Theory of relativityInformation securitySoftware testingDifferent (Kate Ryan album)Programming languageCASE <Informatik>HypothesisTheoryLambda calculusForm (programming)Natural languageSoftwareDescriptive statisticsCrash (computing)Lecture/ConferenceComputer animation
CodeProgramming languageCodeProgramming languageForcing (mathematics)Presentation of a groupMathematicsWordGenderNatural languagePower (physics)Form (programming)NeuroinformatikComputer animation
Power (physics)Programming languageSlide rulePower (physics)Goodness of fitSampling (statistics)Computer animationLecture/Conference
Thermal radiationSpeicherbereinigungNetwork socketThread (computing)CodeSource codeComputer programmingComputer fileRight angleComputer animationLecture/Conference
CodeLatent heatBuffer solutionLine (geometry)Modal logicSet (mathematics)Software bugPoint (geometry)ResultantFreewareFunctional programmingComputer animation
State diagramFunctional programmingException handlingArithmetic progressionNatural numberRight angleCodeSystem callAdventure gameMemory managementComputer animation
Exception handlingState diagramOvalCodeException handlingComputer programmingProduct (business)WordStatement (computer science)System callLecture/ConferenceComputer animation
State diagramLevel (video gaming)CodeException handlingRow (database)HypermediaMultiplication signComputer animation
Multiplication signCodeAreaDialectNumberSpeech synthesis
Constructor (object-oriented programming)Exception handlingSpeech synthesisPattern languageSemantics (computer science)Constructor (object-oriented programming)SpeicherbereinigungDeterminismObject-oriented programmingRight angleData structureSocial classCodeProcess (computing)Computer animationLecture/Conference
State diagramConstructor (object-oriented programming)CodeFunctional programmingSystem callForcing (mathematics)Data structureFerry CorstenBitException handlingForm (programming)Computer animation
Exception handlingDeterminismJava appletClient (computing)Java appletSpeicherbereinigungCodeBlock (periodic table)Object-oriented programmingMultiplication signLecture/ConferenceComputer animation
Object-oriented programmingBlock (periodic table)Object-oriented programmingSpeicherbereinigungCodeSocial classBitFerry CorstenSource codeComputer animation
Open setComputer fileCodeBlock (periodic table)Social classBitState of matterGraph coloringVisual programming languageClosed setSemantics (computer science)Computer animation
Exception handlingDevice driverState of matterElectronic mailing listProgramming languageComputer programmingMultiplication signState of matterComputer animationLecture/Conference
Point (geometry)Interactive televisionData bufferSign (mathematics)Form (programming)Macro (computer science)Ferry CorstenBuffer solutionImplementationCodeUniform resource locatorBlock (periodic table)Electronic mailing listFunctional programmingPattern languageProgramming languageCursor (computers)State of matterText editorPoint (geometry)Selectivity (electronic)Lecture/Conference
Macro (computer science)Block (periodic table)State of matterRevision controlMacro (computer science)Pattern languageBoolean algebraCodeRight angleClosed setComputer animationLecture/Conference
Functional programmingBlock (periodic table)CodePattern languageMacro (computer science)State of matterBlogElectronic mailing listCanonical ensemble8 (number)Computer animationLecture/Conference
Computer-generated imageryMathematicsComputer fileMedical imagingSocial classProgramming languageCodeComputer programmingPresentation of a groupProcess (computing)BitMachine visionVisualization (computer graphics)Computer animation
Streaming mediaComputer-generated imageryBinary fileSocial classError messageExtension (kinesiology)Social classResultantStreaming mediaBinary codeFile formatReading (process)Computer fileContent (media)Exception handlingComputer animation
Streaming mediaComputer-generated imageryInheritance (object-oriented programming)Social classStreaming mediaBlock (periodic table)
Inheritance (object-oriented programming)IterationComputer-generated imageryReading (process)Social classProcess (computing)AreaComputer fileStreaming mediaMedical imagingContent (media)Film editingBitRaster graphicsException handlingElectronic signatureString (computer science)Game theoryTrailPoint (geometry)Reading (process)Forcing (mathematics)HookingPosition operatorSet (mathematics)Message passingBlogArithmetic meanDirectory serviceBinary codeCodeLine (geometry)Computer animation
BlogSocial classInverter (logic gate)Inheritance (object-oriented programming)TrailNumbering schemeStatement (computer science)CASE <Informatik>Image resolutionMultiplication signLecture/Conference
Programming languageImage resolutionDifferent (Kate Ryan album)Computer programmingSet (mathematics)Multiplication signSpacetimeComputer animationLecture/Conference
Solution setProgramming languageSet (mathematics)SpacetimeFunctional programmingCodeSpeicherbereinigungComputer animationLecture/Conference
WritingProgramming languageInheritance (object-oriented programming)SpeicherbereinigungCodeData structureMultiplication signSpacetimeFitness functionFunctional programmingProcess (computing)Computer iconExpandierender GraphSeries (mathematics)Goodness of fitPattern languageImplementationLecture/Conference
Series (mathematics)BlogCode refactoringMonad (category theory)BlogSeries (mathematics)CodeAutomatic differentiationLine (geometry)Functional programmingFactory (trading post)Monad (category theory)Computer animationLecture/Conference
Power (physics)Theory of relativityFunctional programmingSoftwareEvent horizonVideoconferencingSlide ruleDecimalPoint (geometry)Bit rateLecture/ConferenceComputer animation
Transcript: English(auto-generated)
So from this slide, my name is Randy Coleman.
Apparently, since I'm so creative in choosing my online identities, this slide is an exercise in just how many times I can get my name to show up on one slide. As of two weeks ago, I worked for Zeal. So new job. It's kind of fun. Still get my feet under me, but I'm enjoying it a lot. So a lot of us probably flew into this conference.
So you've been on an airplane, long flight, maybe across the country. You get in. You need to find a way to your hotel. And let's say you decided that maybe a bus was the best way to come, because if you're like me, you're cheap, and you maybe don't have the best judgment sometimes. So you take a bus, and you get to where you need to get off. And you're trying to get out the back door of the bus,
and you see this. There's these nice little bars on the side that maybe you think you should push, but no, you have to touch that yellow stripe in the middle. And that's not obvious, so they had to add the yellow stripe with the documentation on it. And apparently, that wasn't even enough. They had to add the white arrows as well, and say, no, you've got to touch here. So documentation on top of documentation. So you finally manage to struggle your way off the bus
with your suitcases, and then you have to cross the street to get to your hotel. And you have this button for doing this. And there's these bright red lights at the top or orange lights at the top that kind of scream, push me, push me. But no, that's not how you cross the street. You have to push that much dimmer button down below.
And because that wasn't obvious enough, they had to add these arrows to point you at the right button to push. So you're starting to get a little frustrated. It's been a long trip. You're tired. You get to your hotel. You just want to get cleaned up a little bit. And you find a sink faucet that looks like that. In theory, you can get water out of that somehow. I'm not quite sure how.
It's not really clear. But fortunately, with this one, they provided instructions. Or not. So at this point, you're about to rage quit life. But fortunately, so what we're talking about here is something called affordances. This is a physical design concept. I first read about it in Donald Norman's book,
The Design of Everyday Things. I actually read it back when it was called The Psychology of Everyday Things. But I think he renamed it for more mass market popularity or something like that. But the idea of an affordance is that physical objects afford certain operations or actions. You've got the crash bars on the doors out there are made for pushing.
Doorknobs are made for turning. Those are affordances. And affordances have also come to mean a little bit how well does an object communicate its intended use? So these ones I just showed you were poor affordances. But what if you walked up to an escalator that looked like that? How many of you can figure that out pretty quickly just by looking at it?
I think it's a brilliant design. Stand on the right, walk on the left. So there are good affordances and bad affordances. That's the physical design world. Affordances, I think, also apply to software. The most obvious place would be in UI design. You want your UIs to be intuitive. You want people to be able to figure out how to use them well. There's a saying that if you have to provide a manual for your website or your web app,
you've lost. And that's probably true in a lot of ways. Affordances can also apply to APIs as well. You want APIs that are consistent, discoverable, easy to use. I've seen examples of APIs where not only do you get your payload back, but you also get a list of here are the URLs that are now valid given the current state of the application.
And so those are also a form of affordances. In this talk, I want to argue that affordances also apply to programming languages. Different languages afford different kinds of solutions. And so by using the affordances of one language, we can solve problems in a way that's kind of unique to that language. But maybe we can take those ideas
and bring them back to Ruby as well. Since this is a Ruby conference, all my examples will be applying back to Ruby. So let's look at an example of what I'm talking about. Let's look at a simple point class. So here's a point class in Ruby. I know you could use a struct. I'm using a class here. You initialize it with an x and a y-coordinate. And then you construct it by newing it up
just like normal Ruby. But what happens if you also wanted to support polar coordinates? I absolutely love the Cartesian bear. I have to keep that slide in this talk. I could change the whole talk, but I have to keep this slide. So if you don't know polar coordinates or you don't remember them, the idea
is that you can specify points like we normally do with an x and a y-coordinate. But you can also specify a point with a distance from the origin, or a radius, or r, it's called, and an angle from the positive x-axis or theta. And there's trigonometry formulas for converting between the two. Those are polar coordinates. They're great for orbital mechanics, radial motion
equations. It makes the math a lot simpler if you use polar coordinates. So if you're writing a library that uses points or a general purpose library for points, you probably want to support both Cartesian coordinates and polar coordinates, just so that the class is flexible enough to use where it needs to be used. So how do we do that in Ruby? We already have our initialized method defined.
It takes two numbers. The r and the theta are also numbers. So how would we distinguish between Cartesian and polar coordinates? We could maybe add a third parameter that's like a tag that says, oh, this is polar. I know this is Cartesian. That's kind of ugly. Let's look at Smalltalk instead. I know most of you probably don't know Smalltalk. I'll try to teach you just enough of the syntax
to understand the code. But this is the identical code in Smalltalk. In Smalltalk, all method names are keyword messages. So you have the colons there. So the x colon and the y colon are the keywords. And the nx and a y are the parameters. And so we have a class method. This is actually the constructor of the point class.
Takes an x and a y. And the little caret is a return. So we're sending self the message new, which gives us back a new object. And then we're sending that new instance and initialize x colon or y colon method. And that's how it initializes. And then that method just assigns the parameter to instance variables. And we construct the object by sending
the x colon, y colon message to the point class. So point x colon three, y colon four at the bottom. So that's exactly the same as the Ruby code I just showed you. So in Smalltalk, supporting polar coordinates is completely obvious. You just add a second named constructor method, r colon theta colon that takes the radius and the angle. This one forwards to the other constructor,
the xy constructor, and constructs the object. And so internally, it's still representing the point as an x and y coordinate. But you can construct a polar coordinate point no problem. So Smalltalk is providing this affordance of named constructors. And it's just a natural way to write constructors in Smalltalk.
There isn't one initialized method. There's just named constructors. And so the solution is completely obvious. It's affordance provided by that language. So can we apply that to Ruby? Well, sure we can. We can write a class side method xy and another one called polar that take the right kind of coordinates. Again, the polar constructor is forwarding onto the xy constructor.
And now we can construct a Ruby point either with xy or with polar. And it's very clear that way using named constructors. For bonus points, I pretended to make the new method private, although in Ruby nothing's ever really private, but at least I'm communicating my attention here. Private class method I think is relatively new, one nine or two or something like that.
So older Ruby's didn't have it. But you can actually do that. So that's one example. Here's another example. What if we want to find something in a collection? Or detect is the other name for it. Smalltalk uses detect. Ruby has both. So here's Smalltalk again. The hash with the parens is a literal array.
And we're looking for an element in that array that's odd. So detect is the message name with the colon on the end. And then the square brackets is a block. And the colon each is actually the block variable. I know it's weird having a variable named each when Ruby you have a method named each. But this is the Smalltalk idiom, so I used it. The one difference from Ruby though is that if there isn't an element
that matches the condition, then it raises an exception, whereas Ruby would just return nil. So here's the same code in Ruby. Exactly identical. I use find here because that tends to be more idiomatic Ruby, whereas detect is used more than Smalltalk. But what if you wanna do something other than returning nil or raising an exception when an element isn't found?
Well, in Smalltalk, there's a variant of detect called detect if none. And you pass a second block to the if none key with the if none keyword. And that block is what gets evaluated if an element isn't found. So in this case, we're gonna return the none symbol. That hash none is a symbol.
So Smalltalk has this affordance of being able to pass multiple blocks to a method. And it's used in a lot of places. Smalltalk conditionals are an if true colon if false method that you pass blocks to, and they're implemented as methods on the true class and the false class. So it's actually a polymorphic message send to do a conditional in Smalltalk, which is a completely different talk.
So I'm not gonna go into any more details on that. But passing multiple blocks in Smalltalk is just natural. You do it all the time. Can we do this in Ruby? Well, it turns out that the find method in Ruby does take an optional parameter, and you can pass a proc or a lambda to it. So it's like passing a second block to it. How many of you knew that Ruby could actually do that?
I see a few hands. I did a version of this talk at Mountain West earlier this year, and there was one hand raised and it was tender love. So not very many people know about this. But it works in Ruby as written. I didn't do anything special to make this work. So you can actually pass a proc or a lambda. I'm using the stabby lambda here, which should make maths happy, I think.
And that lambda will get evaluated if no elements are found that match the pattern. People don't use this in Ruby because I don't think it reads as well as in the Smalltalk. The focus of this code should really be what am I looking for in the list, the main block looking for odd numbers? And this second lambda kind of
takes the focus away from that. So I don't think this code communicates as well. But it's perfectly valid code and it works fine. There's actually another example in active support testing assertions. Assert difference actually can take a lambda or even array of lambdas in it. So there are cases in Ruby where this is used.
It's just not quite as common. But it's a neat trick that you can use when you need it. It is possible to pass a second block-like thing to a Ruby method. So there's a theory called linguistic relativity, or you might also know it as the Sapir-Whorf hypothesis. I have no idea if I'm pronouncing that correctly. But that's what it is.
And the idea is that the language you use influences the kinds of thoughts you have or the way you conceptualize your world. Now there's a bunch of debate about whether this is actually a valid theory or a valid description of the way things are. There's a strong form and a weak form and you can go read up about that on Wikipedia like I did. But the idea is that our language influences our thoughts.
Now this is talking about natural languages. But I think this also applies to programming languages. Corey Foy did a talk at Software Craftsmanship North America a couple of years ago called When Code Cries, which I love the title of the talk. It's awesome. But he asked two really interesting questions in that talk. And one is, what does a language allow you to say? What kind of ideas can you express in the language?
And also, what does a language force you to say? Does the language force you to say things that you don't want to have to say? And he used an example from natural language where some languages have gendered nouns. So if I was talking to you and I said, oh, I went to a movie with my neighbor. You might, especially if you're my wife, want to ask me, well, was your neighbor male or female?
And unless you're my wife, I'm probably going to say, well, that's really none of your business. But if I was speaking to you in French, where they have gendered nouns, then when I use the word for neighbor, I would instantly be telling you whether my neighbor was male or female because of the gender form of the noun that I used. And so French is forcing me to say something that English doesn't force me to say.
And that's also true in programming languages. Some languages force you to say things that you don't want to say. For Java, for example, you have to say your types over and over and over again. But also, what languages allow you to say is important as well. Now, all of our languages these days are basically true and complete. You can express any computation in them, but they're not all equally expressive,
and that's important. When I was researching this talk, I came across an old presentation by Matz at OSCON in 2003 talking about the power and philosophy of Ruby. And one of the things he said on one of the slides is languages are not only tools to communicate, but also tools to think. And he wanted Ruby to be a good thinking tool,
and I think he succeeded very well. But that's important. So the languages we use influence the thoughts we have and help us to think better thoughts sometimes. So let's look at a few more examples. When we're programming, we often have to clean up after ourselves. Now, we have garbage collectors now, so that helps with one kind of cleanup.
But there are other kinds of resources we have to clean up, file handles, socket handles, threads, locks, et cetera. And so we often have to worry about cleaning up after ourselves. We have to free up resources when we're done with them. So I'm going to show you some really nasty C code. So there's a warning. Fortunately, there's a party right after this,
so you can kind of go drown the sorrows of what I'm about to show you. How many of you have seen C code that looks like this, or this specific C code? Yeah, there's more hands than probably you're comfortable admitting there are. Anybody recognize this particular piece of code? Yeah, there's a few. This was an SSL bug that Apple had
back eight or nine months ago. It's called the go-to-fail bug. And the problem is that somebody deleted a line of code in the middle there. And because this code uses a particularly egregious set of coding styles, it introduced a bug. But the whole point is that there was a necessity to clean up, no matter how we finish this function,
we had to go down to the bottom and free up those buffers at the end. So there's got to be a better way than this that doesn't result in bugs like this. And I'm not going to try to clean up Apple's code. I'm going to give you a slightly more simple example. I have this very expressively named function called foo, or a method called foo. And in it, we have to acquire some resource, do some things with it,
and then release the resource when we're done. Now, there's at least three or four ways that you can get out of this method, and only one of those ways is actually going to release the resource when it's done. And that's the natural progression right through the end. There's an early return in there, that's the obvious way that you can get out. But also, any of the methods I call in there might raise an exception. And if they do, I'm going to leave this method,
and I'm going to blow up and not free my resource. So I've acquired the resource, and I've caught the exception at an outer layer, and I did not free my resource, I just leaked it. And if I run this code a lot in my system, eventually I'm going to run out of resources, and my program's going to blow up in production, and that's not cool. So we have to write our code differently
to protect ourselves against these problems. So here's one way you could do it that actually solves the problem, it releases the resource when the exception is thrown. But look at what we had to do. We had to change the early return into a nested if statement, and so now we've got what I call wedgie code, where it's all shaped like a wedge
because of all the levels of nesting. We had to catch an exception that we really don't care about here, and then re-throw it. We had to duplicate the code for releasing the resource. Those are like three ugly things we had to do to this code, and I'm not saying the original code was all that great, but this is worse. Feels to me like running a stove like this,
where if you want medium heat, you have to press a button like six times to get it. Fortunately, they give you two buttons, so you don't have to press it 12 times to get high heat, but still. Or maybe dialing a phone like that, anybody remember those? Interesting thing I read once is that the bigger cities in the US have area codes using lower numbers, like New York is 212 and Chicago is 312.
I think LA is 213 if I remember right, and the idea is they were actually optimizing for the more frequently dialed area codes. They figured the more populous places, those area codes would be dialed more often, and so they made those as efficient as possible with the dial. That's totally a side note, but I thought it was kind of cool when I read it.
So what can we do instead? We can use an idiom from C++ called resource acquisition is initialization, which speaking of affordances is not a well-named idiom at all, but the whole idea is that you acquire your resources in constructors and you release them in destructors, and the reason that works in C++ is because C++ has deterministic destructor semantics.
When an object goes out of scope, its destructor is called immediately right then. It's not later when a garbage collector runs. It's not never. It's always right when that scope ends, and it doesn't matter whether the scope is ended normally by just exiting out of it or abnormally by raising an exception. It always calls the destructor right then,
and so there's a pattern you can use in C++ that takes advantage of that, and so we can write a safe resource class, and safe resource, we acquire the resource in the constructor. We release it in the destructor, and I added the get method so that other code can work with the actual resource itself, and then we can look at our foo method,
and now we create a safe resource at the top of the function. On the stack is what it's called in C++, and when that function exits, safe resource goes out of scope, its destructor gets called, and it releases a resource, and it doesn't matter how we exit the scope. That will get called, and so now our code looks basically like it did before.
There's a couple extra get calls in there, but it has the same form as it did with the early return. We're not catching the exception we don't care about. We don't even have to call the release call anymore because the destructor does it for us automatically, so this is even a little bit better, and this works. It releases the resource. So we have this affordance from C++ called deterministic destructors.
Can we take that affordance and the solutions that it allows us to create and apply those to Ruby? Well, Ruby doesn't have deterministic destructors, so what can we do instead? Well, we could use begin and ensure everywhere, which is kind of ugly. Every client would have to do that. It's like using try and finally in Java, and if you've ever read much Java code, there's tons of that littered everywhere.
I don't like that idea very much. You can actually define a finalizer in Ruby. You can define a method that will get called when the garbage collector finalizes an object. That's not quite deterministic because it only happens when the garbage collector runs, but you can do that. Not very common. People don't do it very much, but Ruby has blocks,
and so maybe we can do something with blocks instead. So here's a safe resource class in Ruby. There's a class side method called acquire that allocates the resource, acquires the resource, yields the resource to a block provided to this method, and then has an ensure that releases a resource when it's done, and then initialize and release are the acquire and release of the resource.
And we can use that in our foo method, just like we did in C++. We have to do it a little bit differently where we have to use this class side acquire method. So we can say safe resource dot acquire, and then the do end is the block that we're passing to it, and that code will get executed while the resource has been acquired, and when the block exits, whether normally or abnormally,
the ensure block in the acquire method will make sure that we release the resource when we're done. So this is very similar to the C++ code. We're using blocks to give ourselves those semantics. You've probably used this idiom before in your Ruby code. If you use file open that takes a block, it opens the file. The block executes while the file's open, closes the file when you're done.
There's lots of places in Ruby where this idiom is used, and that's basically the way you solve the problem in Ruby, and actually Smalltalk as well uses the same idiom for this. Blocks are great for this. And just showing you that that works as well. All right, another example, kind of similar to cleaning up after yourself,
and this is places where you need to restore state. So for example, in a graphics program, you might want to use a certain pen or a certain foreground color, do some drawing, and then set that state back to where you were. This time I'm actually gonna use emacs list for my example language, because I'm used to program lists years ago, and I love it. It's a pretty cool language.
So here's some code where we want to delete some text between a couple of delimiters. And so the interactive there just means that this is code that's gonna be executed interactively by the user. It can be, it's not just a function to be called by other code. Emacs list is the language used to customize the Emacs editor, by the way.
And then there's the save excursion thing that I'll talk about in a minute. And within the code, we're just skipping backwards to find a delimiter, and then skipping forward to finding a delimiter, remembering the two places where we found those delimiters, and then deleting the region between those two points. That's what that code's doing. What save excursion does is it basically remembers
the state of what buffer you're in, where's the cursor located, where's the selection, all that kind of stuff. And once save excursion is done, it restores that state back. So in Emacs Lisp, when you're writing functions to manipulate some text or find things, you can put everything in a save excursion, and then you can go all over the buffer, do whatever you need to do,
and then save excursion will take you back to where you were. And the way it works is, now the actual implementation is in Emacs C code, but it works as if it was written as a macro. Lisp macros are really cool. They're probably more powerful than most things you've seen. There's a little back quote there that basically quotes all of the rest of the form there.
And so when the macro is expanded, it's like it generates that code that's protected by the backtick. So what we're doing is we're remembering the current buffer, the current location of the point, the current location of the mark, which is another Emacs concept. Unwind protect is like an ensure block in Ruby.
Prog n says here, take a list of forms and treat them as one form and return the value of the last one. And then the comma at sign basically splices in the body we provided to this method into that location in the macro. And so this is gonna expand out to code with the let form, the unwind protect, then all of the code that we provided in the body of save excursion.
And then the three forms at the end are what's done by unwind protect as that code exits. So you don't need to understand the list very much, but this is a very common pattern in a Lisp macro where you do some stuff, splice in some forms, and then do some other stuff.
Okay, so we have this affordance of macros in Lisp. What can we do in Ruby? Well, the answer again is blocks. Blocks help us here again. So we can write a version of save excursion in Ruby where we remember the state at the beginning, we yield to a block, and then we ensure that we restore the state at the end. And so again, we've been able to use Ruby's blocks
to substitute for at least one kind of macro that you might write in Lisp. And there's our delete and close text where we call save excursion, we pass it a block where we do the work we wanna do, and save excursion takes care of restoring the state back where it need to be.
So any kind of code where you write where you kinda want something in a certain state while you run the code and then you wanna automatically restore it back at the end, again, you can use this pattern with blocks very much like Lisp macros. There's some other examples in this blog post I read recently by Kevin B. Cannon of 8th Light where he takes a couple of other Lisp macro ideas
and applies them back to Ruby, again, using blocks. Interesting article to read. I have one last example for you, and this is the idea of an image reader. This is again from Smalltalk. I programmed Smalltalk for like 13 years before I started my new job two weeks ago, so I like Smalltalk a lot. It's a great language. But the image reader idea is we've got this base image reader class
and we want it to be able to read any kind of image, and we wanna be able to even support images we haven't even thought of yet or we haven't implemented yet without having to change our image reader class. So this is code actually in Visual Work Smalltalk base image. I've changed it a bit for presentation purposes, but it's essentially the same.
This is a little bit of a wall of Smalltalk, but what this is is a class side method called from file. You pass it a file name. It opens a binary read stream on the file, and then it sends itself the reader class four method with that stream and assigns the result to reader class. If you can't find one, it raises an exception,
but otherwise it instantiates the reader class on the stream and then closes the stream when it's done. So the idea is it's gonna search somehow for a class that can handle this format of file, and we're not looking at file extensions here. We're actually looking at the contents of the file. So reader class four takes the stream,
and what it's doing, a Smalltalk class knows all of its subclasses. So you can ask actually for its direct subclasses, which is what we're doing here, or all of its descendants, there's an all subclasses method that does that. And so we're asking the class for its subclasses,
and then we're doing a detectif none, which we saw earlier. So we're looking for a class that satisfies this first block. We're resetting the stream back to the beginning, and then we're asking the class, hey, can you read this stream? And the class is gonna return true or false, depending on whether it understands that format or not. And when we find one that answers true, that's what we return from this method.
So that's how we find our reader class. So we have this affordance in Smalltalk again of being able to ask a class for its subclasses and iterate them all. Can we do this in Ruby? Well, let's see. We have a read method on the class side. It takes a file name, opens a binary stream on it, and then calls this find reader class method
with the stream. It raises an exception if we can't find one, otherwise creates the class on that stream. So far it looks pretty much like the Smalltalk. Well, here's find reader class. How does that work? Saying subclasses.find, so reason find instead of detect, and asks the reader class, can you read this stream?
Again, very much like Smalltalk, but how do we get subclasses in Ruby? Ruby doesn't keep track of that for us, does it? No, it actually doesn't. But we can simulate it with two little methods here. So a class has a hook method that's called when a subclass is defined. And so when you define a subclass of image reader, this hook method inherited will get called,
and it'll pass in the subclass that was just defined. And so what we can do is we can shovel that onto a lazily initialized array and remember it. So this class is keeping track of its subclasses as they're defined. And that's how we can iterate them. We just, we're calling the subclasses method and iterating them all. If you're using active support,
it actually does this for you. There's a subclasses method defined by active support. There's also a descendants method that gives you all of the descendants of the class. So active support is doing this for you, but you can do it yourself in plain Ruby with these two methods. And so we can write a bitmap image reader, and all we have to do is implement the can read method,
and it's looking for the bitmap signature, which is the first two bytes have to be VM. The read image is the method that would actually read the image here. I'm just printing out a string so you can see it's doing the right thing. There's a JPEG image reader. It looks for the JPEG signature, and that dot B on the end of the string there is because encodings. That's about all I have to say about that.
And we can have a ping image reader, which looks for the ping signature, which is actually eight bytes long, and it's kind of actually fascinating. There's things in there to detect byte ordering and carriage return line feed conventions and so on. That whole signature has meaning there.
It's there for a very particular reason, which I found kind of interesting. And so we can run this code over a set of files. I ran this over a directory with one of each kind of file and it reads each one by just looking at the content. And so because it's looking at stream content, you can even use this class directly on a stream you're reading back from like an HTTP endpoint
or something like that. Getting back image data, you don't even have to have it in a file if you don't want to. So that's kind of cool. Very similar example is a blog post by Andre Bernardes I just read a few weeks ago called Inverting Dependencies where he's trying to solve a very similar problem. The way he solved it is the subclass,
when you define it, it would actually call a method on the base class to register itself with it. You could solve it the way I did here with just keeping track of subclasses as well, but it's very similar problem. You see this from time to time. I've seen it for like a URI scheme handlers where you want to define new handlers as a subclass of a basic handler. And you really don't want to have to go back
to the base class and change it like to add a case to a case statement or something like that. So this technique is great for that, where it basically, as long as you have something that requires in the class that you need, it kind of automatically participates in the resolution of what you're trying to resolve. So it's kind of cool. So what have we learned today? The first thing is that languages afford certain kinds of solutions or designs
and inhibit others. So you just naturally solve problems certain ways in certain languages. And other solutions don't come so naturally in those languages. Our languages influence our thoughts. So when we're programming all the time in one language, we think one way and we solve problems one way.
Whereas if we use a bunch of different languages, it expands our thoughts. And so by learning new languages, you can increase your solution space. So when you're trying to solve a hard problem, the more things you have to draw from, the wider the set of solutions you'll have to consider, and the more creative your solutions will be. You'll come up with better ideas, better designs, because you know more things.
So I guess my encouragement is to learn as many languages as you can. And even if you don't ever use them for real code, just becoming fluent enough to understand how to think in them will help you with your Ruby or whatever other languages you're working in. But don't go too crazy with it. You can get a lot of great ideas from functional programming, for example.
But if you tried to go full on immutable data structures in Ruby, you're gonna give the garbage collector fits. Other languages are much more optimized for that kind of thing. But certainly take the ideas that work and don't go too far. You also want people to be able to understand the code you write. And you want to use the language for what it's good for and its strengths.
Years and years ago, I was forced to write code in VB6 and I'm almost done with my therapy finally. But VB6 does not have implementation inheritance. And back then I didn't know this, but implementation inheritance is quite overrated. But when you need it, you really need it. And so I came up with a way to kind of simulate it in VB6 using composition and delegation.
And I'm not sure my coworkers really understood my code all that well. I kind of taught them the pattern, but I'm not sure they really got it. That was probably too far. So don't go too crazy with this, but certainly take the ideas. It's amazing how many times I've learned something that doesn't seem related to my job. And yet within two weeks, we'll come across a problem that's like, oh, I just read about this thing
and it works here. This happens all the time. So just learn what you can and expand your space and your ideas. If you're interested in this topic, if I caught your attention a little bit, I actually wrote a whole series about this on my blog. I cherry picked the examples that applied to Ruby, but I kind of gave some other examples that applied to other languages as well.
Tom Stewart did a great talk at GoGuruCo a couple months ago called Refactoring Ruby with Monads. Probably the best introduction to Monads I've ever seen, and also is very much along these lines where you can take the Monad idea from functional languages and apply it to your Ruby code to come up with better solutions. So I highly recommend checking that talk out.
I want to thank Zio for sending me here. And there's references to everything I talked about. Thank you, Adam. I'll have the slides up on speaker deck later today or tomorrow. And I'm on speaker rate. If you want to give me feedback on the talk, I'd appreciate it. I'm always trying to get better. Thank you very much.
Enjoy the party tonight. Thank you.