November 27, 2025 8:10 PM
Fizzbuzz, Functional Style
Ask, and ye shall receive.
In yesterday's post, I reported on an attempt to solve the classic Fizzbuzz problem in a functional style. Near the end of the post, I wrote:
This solution is fun but feels a little unsatisfactory. I'm not as fluent with functional programming as I am with OOP. Perhaps there is a more idiomatic FP way to do this? Let me know.
Thanks to everyone who responded with ideas and conversation!
Chris Johnson offered a couple of different approaches and ended up with a slick solution that is fully functional: create an array containing the different values to print, then use cycled values for Fizz and Buzz to construct an index into the array.
Here's an elegant solution in Haskell, courtesy of Chris:
pick :: (Int, Int, Int) -> String pick (f, b, n) = [show n, "Fizz", "Buzz", "FizzBuzz"] !! (f + b)main = do let fizzes = cycle [0, 0, 1] let buzzes = cycle [0, 0, 0, 0, 2] mapM_ putStrLn $ take 30 $ map pick $ zip3 fizzes buzzes [1..]
Beautiful. 1 (= 1 + 0), 2 (= 0 + 2), and 3 (= 1 + 2) select the appropriate word to print in cases divisible by 3 and/or 5, and 0 (= 0 + 0) selects the number as a default. Beyond that, the code is a couple of maps on top of a zip.
This technique reminds me of a similar trick Joe Bergin used when we ran the Polymorphism Challenge workshop back in 2005, solving one problem by looking up in an array an object to send a message to. I'd completely forgotten that solution until I saw Chris's!
Racket is not as concise as Haskell for a problem like this, but a Racket solution is nice, too.
First, here's choose:
(define (choose f b n)
(let ((answers (vector (number->string n) "Fizz" "Buzz" "FizzBuzz")))
(vector-ref answers (+ f b))))
Then we define the cycles:
(let ((fizzes (cycle '(0 0 1) size))
(buzzes (cycle '(0 0 0 0 2) size))
(numbers (range 1 (add1 size))))
...)
Finally, the code to implement the functional idea is:
(map displayln
(map (lambda (L) (apply choose L))
(zip fizzes buzzes numbers)))
I have to apply choose to the zipped lists because
Racket doesn't have a destructuring define form.
Of course, Racket is a language for making languages, so we
can create one if we'd like! I've written a three-place form
hardcoded to handle (define-match (choose (f b n)) ...),
but I'll have to do some work to generalize it to take lists of
any size.
Thanks again to everyone who wrote in response to my post, and especially to Chris for the fine solution. Readers of this blog continue to teach me new things. I hope they find some value in my posts, all too rare these days. In any case, I have much to be thankful for on this Thanksgiving Day.
November 25, 2025 1:33 PM
A Polymorphism Challenge, Functional Programming Style
in the Fizzbuzz language
Over the weekend I ran across a blog post by Evan Hahn in which he took on this exercise:
... write Fizz Buzz with no booleans, no conditionals, no pattern matching, or other things that are like disguised booleans.
Ah, the memories! In 2005, Joe Bergin and I ran a SIGCSE workshop
called The Polymorphism Challenge, which I mentioned briefly in
a trip report
at the time and reported more fully in
a 2012 post.
The goal of that workshop was to help participants learn how to use
polymorphic objects to solve a variety of standard problems without
relying on if statements to select among different
conditions. It was great fun, and many of the workshop participants
found it both challenging and enlightening.
Hahn's post gives a functional solution to Fizzbuzz in Python, using iterators and a creative, though limited, string mask:
from itertools import cycledef string_mask(a, b): return b + a[len(b):]
def main(): fizz = cycle(["", "", "Fizz"]) buzz = cycle(["", "", "", "", "Buzz"]) numbers = range(1, 101) for f, b, n in zip(fizz, buzz, numbers): print(string_mask(str(n), f + b))
I thought, hey, I can do that in Racket:
(define string-mask
(lambda (num-str fb-str)
(string-append fb-str
(guarded-substring num-str
(string-length fb-str)))))
(define fizzbuzz
(lambda (max)
(let ((fizz (cycle '("" "" "Fizz") max))
(buzz (cycle '("" "" "" "" "Buzz") max))
(numbers (range 1 (add1 max))))
(for ((f fizz)
(b buzz)
(n numbers))
(displayln (string-mask (number->string n)
(string-append f b)))))))
Two notes:
-
I wrote my own
cyclefunction. Racket has asequence->repeated-generatorfunction inracket/generator, which behaves likecyclein Python'sitertools, but I wanted to write a simpleforover lists. -
I had to create a substring function that allows the
start index to be out of bounds. Racket's native
substringis more careful than Python's [:].
The string masking idea is clever, but Hahn points out that it starts to fail when the number is long enough that the Fizzbuzz string can't cover it all.
This solution is fun but feels a little unsatisfactory. I'm not as fluent with functional programming as I am with OOP. Perhaps there is a more idiomatic FP way to do this? Let me know.
Bonus code: If you want to see a solution that will melt your face, check out this article implementing Fizzbuzz in the array language Q. I learned APL on 1985 or so, and its way of thinking will always have a strange hold on my brain!
Anyway, that was a nice diversion for a few minutes during Thanksgiving break. As I tell my students almost every day in class, I like to program. I'm thankful that I am able to do it.
October 05, 2025 1:40 PM
On My Way, Rejoicing
This passage is spoken by the protagonist in Muriel Spark's 1981 novel Loitering with Intent:
I found the book on the library shelves and while I was there in that section, I lit upon another book I hadn't seen for years. It was the /Autobiography/ of Benvenuto Cellini. It was like meeting an old friend. I borrowed both books and went on my way rejoicing.
I don't know anything about Cellini or his autobiography, but I know this feeling well. Stumbling onto an unexpected book at the library gives me almost unreasonable joy.
Loitering with Intent is my second Spark novel, after Finishing School. I gave Spark a try on recommendation of her work by Henry Oliver at The Common Reader. Oliver also reignited my interest in the novels of Penelope Fitzgerald, whose The Bookshop I first read years ago... after stumbling across a copy on the shelf of my local library. Fancy that.
September 08, 2025 3:52 PM
An Incredible Triumph of Human Cooperation
I went to this blog post by Jordan Ellenberg for the baseball (well, that's not quite true — I read all his posts) and left feeling a kinship with him in this regard:
Wikipedia is really an incredible triumph of human cooperation. Why does it work so well? How is it so unpolluted? How is it that when some very weird niche question like "which pitchers have beaten all 30 teams?" comes to my mind, some human being has already compiled this and put it there? I don’t know. But I'm glad it exists.
I remember back when Wikipedia first began, how so many people were convinced that it could not work. Having been active on Ward Cunningham's C2 wiki in the 1990s, I knew that it could, but I wasn't sure how the idea would scale to include anyone and everyone with net access.
I grew up reading the encyclopedias on the basement shelves in our family home, trying to soak up everything under the sun. How could a collaboratively built website with no authoritative gatekeeper match those volumes?
Nearly twenty-five years later, I can say that Wikipedia has succeeded beyond my imagination. Sure, it faces challenges with some contentious topics. However, for almost anything I might want to learn something about — even weird niche questions like "Which pitchers have beaten all 30 teams?" — I usually count on Wikipedia for an answer. People love to build and share knowledge.
August 31, 2025 9:31 AM
Another Old Person in the Room of the Cybersecurity Knowledge
This passage from an interview with Lesley Carhart was a shot in the arm I needed:
You've spoken about the risk of younger generations relying too heavily on tools and AI without learning the basics. Are you worried we're losing critical knowledge?
I struggle sometimes with how to convey this without sounding like the "old person" in the room. But there really is a problem. Since I started in this field, universities everywhere have launched cybersecurity degrees, and many of them are fundamentally flawed because they don't teach the foundations of computing.
We now have a generation that hasn't been exposed to the inner workings of computers. Everything is point-and-click, tablets, and touchscreens, so they don't learn at home how computers actually work. Then they enroll in cybersecurity programs, but those programs also don't teach fundamentals. Instead, they teach how to use tools—EDR, Metasploit, whatever's current. And tools change constantly. In cybersecurity, tools and techniques are outdated within a few years. Without foundations, students can't adapt, and they can't work with legacy systems. That's a big problem.
I am not a specialist in cybersecurity, so it's heartening for me to hear someone with Carhart's experience and expertise say this about cybersecurity curriculum.
My department has offered a degree program called Networking and System Administration (NaSA) for over twenty years. With the rapid growth of cybersecurity programs at community colleges, and increasing interest from high school students, there is a strong desire among admissions staff and upper administrators for us to offer a cybersecurity program. The NaSA major has long taught some of the essential skills of cybersecurity, so we are evolving it to focus more directly on cybersecurity.
However, our program will continue to teach the fundamentals that Carhart says are important. It will have a small intro CS core, plus standard courses in operating systems and networks — and statistics, knowledge of which is essential for studying system behavior and machine learning.
This focus on fundamentals creates challenges. It's hard to make room for all the tools and techniques that students need to know now. But, as Carhart says, many of those tools and techniques will be outdated within a few years. We are preparing students for long careers, and to be contributors and citizens in a complex social world. Even if we approach the task from a purely technological perspective, though, without foundations, students would struggle to adapt to the changes we know are coming.
A foundations-centered program also creates practical challenges in the world of university admissions, because it makes it harder for community college students to parlay their CC degrees into immediate university credits. That's a big deal in a world where enrollment drives university budgets and CC students are a primary audience.
We're working on all of these challenges. We are slowly figuring things out. But it's encouraging to know that an expert such as Carhart appreciates the need for cybersecurity students to learn the foundations of computing. We think they are important, too.
May 30, 2025 1:54 PM
To Experiment Is Human
From a Robin Sloan newsletter:
I wonder, finally, if one of the key markers of human-ness, and human intelligence, is: the experiment.
All of these activities are points on the same continuum:
- troubleshooting a car
- debugging a computer program
- devising a science experiment
In all those cases, there's a particular kind of creativity at work: engineering these lenses, these filters, these ... tweezers? to isolate new, reliable knowledge about the world.
Many people, including prospective CS majors and their parents, ask me what it takes to succeed in computer science. How will I know if computer science is right for me?
One of things I talk about is curiosity: a desire to figure out why something isn't working (or is), a desire to change a system to make it work in a desired way, a desire to learn new tools and techniques. Whatever other skills and attributes a person brings with them, the curiosity to tinker and experiment goes a long way in our discipline.
We have students with a lot of different career goals. This answer applies to all of them. Curiosity is part of the mindset of all CS majors whatever there role ends up being after they graduate: software developer, sysadmin, database administrator, someone working in cybersecurity...
I'm guessing physicist Chad Orzel might agree. He wrote Eureka, an entire book about how almost everyone, even someone who thinks they don't like science, is doing science all the time. A big part of that unspoken process is testing our mental models by comparing them to reality. For computer science students and grads, this mindset is front and center every day.
Summer orientation starts next week, so I'm getting into the mode of reviewing the backgrounds of incoming students and preparing to advise them. I imagine I'll be discussing curiosity and experiment in my conversations over the coming weeks with a few new majors who are excited to dive into CS this fall.
May 26, 2025 6:14 PM
Feeling That Old Excitement
I am teaching compilers again this fall for the first time since 2022. After turning my grades for spring in last week, I started to feel the old buzz... How could I tell?
Everywhere I look I find ideas and images that trigger thoughts of the course.
I am starting to notice new languages that inspire the syntax for a source language my students might write a compiler for, such as Lil, the scripting language for Decker (a browser-based environment inspired by HyperCard).
I'm starting to notice ideas for integer-oriented programs to write in whatever source language we end up working with, such as Leyland numbers (x^y + y^x for integers x > 1 and y > 1) and left-truncatable primes (a number where you get a prime number no matter how many leading digits are omitted).
I'm starting to notice ideas that inspire my desire to create a fun and valuable experience for my students, such as Austin Henley's post on group projects.
That post introduced me to a great line from Russ Cox:
Software engineering is what happens to programming when you add time and other programmers.
I recently ran across Cox's wonderful article on regular expression matching again and stashed it away in my exuberance to improve the lexical analysis unit of my course.
Henley's post echoes one of my goals for the course:
... for students to have experience using "modern" tools in as "real world" of a setting that we can possibly emulate in a course, giving them a significant project for their portfolio.
I approach this goal from the perspective of a "compiler writing for everyone" course, a project course that appeals to and benefits undergrads with all sorts of career goals.
Doesn't this mean that my students will be reinventing the wheel? Maybe, but there are many good reasons to reinvent the wheel.
Did I mention that I currently have fifteen students enrolled in the course? Maybe the students are as excited to revive the course as I am.
This will be the first time I've taught the course since the explosion of LLMs in coding. What will I do with or about these new tools? What should I want my students to do?
I'll admit, this is a dark cloud on the horizon for me.
Fortunately, I'm also starting to notice articles other folks are writing about how they are incorporating the ubiquity of this new technology into their teaching. This slide deck from the Educational Summit at 2025 PyCon, for example, gave me a few ideas to consider.
There is much to do. Let the summer begin!
Posted by Eugene Wallingford | Permalink | Categories: Computing, Software Development, Teaching and Learning
May 14, 2025 4:21 PM
Mile 22
Matt Webb learned something when he ran a marathon last month:
What this says to me is that when you go to extremes, new things happen?
There are new experiences to be found, when you go past your limits, which aren't like the old ones scaled up. They're something distinct. Unanticipated and unanticipatable.
I don't know how to explain it better than that! It seems to me that this is true of so many things. The only way to know what it's like to run mile 22 is to run 21 miles first, you can't shortcut your way there.
The last sentence is so incisive that I almost paraphrased only it: "The only way to know what it's like to run mile 22 is to run 21 miles first. You can't shortcut your way there."
— or a full?
I remember that feeling as a runner, pushing past different limits. The one I remember most was fifteen miles. For some reason, the first time I ran mile 16, it was a completely new something for me. I seemed to revisit that barrier each time I tried to bump my speed up to a new level. Running mile 16 at the new pace was harder than any mile before it, or after. Even in my last couple of marathons [ 1 | 2 ], Mile 16 was the challenge that ultimately determined what my day was like. (*)
I'd love to have had one more chance to break on through to the other side...
A lot of CS students learn that there are new experiences waiting for them when they push past what they thought were their limits. One of my Programming Languages students took the time to tell me so last week.
On Friday, students submitted Part 3 of a three-part assignment in which they implemented an interpreter for a simple arithmetic language containing numbers, unary and binary operators, local variables, and blocks with assignment statements. They implemented abstract syntax, a preprocessor for the syntactic sugar, and an evaluator. To support variables and assignment statements, they implemented simple data structures for the environment and mutable cells.
This is the biggest program many of them have every written. To further the challenge, they were writing code in functional style using Racket, a language they had started learning only fifteen weeks prior.
One student included a comment block of several lines in his final project submission to tell me how much he had learned beyond the stated goals of the project and the course. It turns out that writing a much larger program is different than writing small programs for a class. Structure matters. Names matter. You have to revisit decisions, learn new language primitives, and change how you think about software. He hadn't expected to have these experiences at the start of the project.
Some experiences aren't like the old ones scaled up.
The only way to know what it's like to run mile 22 is to run 21 miles first.
~~~~~
The latter of those two posts has one of my all-time favorite blog post titles: "Because 26.3 Miles Would Be Crazy".
Posted by Eugene Wallingford | Permalink | Categories: Personal, Running, Software Development, Teaching and Learning
April 28, 2025 12:10 PM
This Week in "Side Projects That Inspire and Amuse"
Earlier this week, someone boosted Dan Vanderkam's After 20 Years, the Globally Optimal Boggle Board into my Mastodon feed. Thank you to whoever did. (*)
This article makes me happy. I have not played Boggle all that often, but it's a fun game, and I certainly understand the obsession. Vanderkam has spent a couple of decades studying a pet problem, applying any tool from CS that would help, including ideas from data structures, algorithms, and parallel computation, to finally answer the question that has been propelling him forward.
Even better, he has been blogging about his efforts all these years, keeping his readers informed of the fits and starts he faced along the way. I only wish I had discovered his blog sooner so that I could have been one those those readers sooner!
Even though I'm not much of a Boggle player, I love pretty much all such games. They are fun for personal diversion when I encounter them in the wild, and they make for great source material when teaching. Games like Boggle are great projects in an OO programming class, especially one that includes simple graphics. The technical elements of the author's project offer fun ways to introduce concepts and challenges in courses on data structures, algorithms, and classical AI.
The article also inspires me as a computer scientist. Now I want to go back to some work I did on progressive chess with a student a few years ago. Progressive chess is a variant in which players make increasing sequences of moves: White moves once, then Black moves twice, then White moves three times, etc. One would think that, as each player's turn gets longer, it would become increasingly easy to deliver checkmate. However, a player can also use their sequence of moves to weaken the opponent's forces and to defend their own king.
I have all kinds of questions about the game, including the first question researcher usually have about two-person adversarial games: Is there a forced win for either side? Techniques from state space search offer a starting point for trying to answer the question, but how much will they have to be adapted in order to the scale the depth and breadth of the search tree?
Reading Vanderkam's article has me wanting to revisit the topic. I'll also be reading some of his older posts to see what other lessons he learned along the way.
~~~~
(*) I really have to do a better job keeping track of where I get the articles I write about, so that I can properly thank the person responsible for me seeing them. That would be easier if I knew immediately which articles I want to write about — or if I kept track of the source of every link I set aside for later!
April 10, 2025 4:20 PM
Set Phasers to Stun
In the notes for my class Tuesday, I made a reference to Jean-Luc Picard, commander of the Enterprise in Star Trek: The Next Generation.
I described a function that I would like to have. "Make it so", says Picard from the flight deck.
Then it struck me. Star Trek: The Next Generation is nearly two decades older for my students than the original Star Trek was for me and my classmates.
Sigh.
My only hope for not sounding to my students like a dinosaur walking the Earth is that the Star Trek universe is still reasonably popular in American culture. Young people today may think of Paul Wesley or Chris Pine when they think Captain Kirk, rather than William Shatner (*), but if they think of Picard, they think of Patrick Stewart.
(*) the one true Kirk, with no disrespect to Messrs. Wesley or Pine
Time moves on.
I'm not the only one these days noticing the disjunction between how we perceive time and age. Daniel Steinberg reminisced in a recent essay about a job he held whe he was 24 years old:
One of my closest friends was an older woman named Rachel (actually it's funny, she was thirty years older than me but I'm now more than ten years older than she was then and I always thought of her a nice old lady).
Daniel and I are now the nice old people that twenty-somethings know and like today. Back then, I never considered what it would feel like when I was that age.
To be honest, now that I am that age, I feel pretty much the same as I did then. And I'm okay with that.