Latest Posts (20 found)
Lambda Land 1 weeks ago

How I Organize the Papers I Read

I got asked how I manage papers, notes, and citations for doing research. I started writing out a very long Slack message, but it quickly passed the threshold where I ought to just turn it into a blog post. The short of it: I’m an incorrigible Emacs user, so I do a lot through my editor of choice on my laptop. That said, Zotero is a fabulous piece of technology, and I rely on it heavily to get my work done. Use Zotero in some capacity. Zotero is great. You should use it at a minimum for collecting papers and keeping paper metadata. It’s completely free and open source. It has excellent apps for iOS and Android so you can read and markup papers on a tablet and access everything on your desktop, but that’s optional. It’s so smart about finding citation information: drag a PDF into it and it will look for the DOI or something and auto-populate the relevant bibliographic information. It’s not perfect, but it’s still pretty darn helpful. When you’re starting out, I recommend using Zotero’s hosted syncing purely because it’s so easy to use. If you start being a paper packrat and need more than the 300 MB limit, you can self-host or pay a little for more storage. (I’m using 797 MB after several years of heavy Zotero use—I even have a few books in my library!) The lovely thing is you don’t have to commit to syncing up-front . You can start with purely local storage too if you want. If you’re a LaTeX user like me, you should use the Better Bibtex package. You can configure it to make a file for your entire library or just certain collections. I keep a big file for my entire library and then separate files for each paper I write. As long as I am the sole author, that is. My advisor prefers manually managing bibliographies, so what I tend to do is manually copy the reference information from my main file into the file for our shared paper. I’m as close to an Emacs maximalist as you will find. Nevertheless, I prefer reading and most note-taking outside of Emacs. I read and annotate papers on my iPad, and Zotero syncs the annotations to my desktop. When I’m writing papers, I use the Citar package in Emacs. This makes it easy to find references and insert citations. Works for Markdown, Org-mode, and LaTeX files. If you’re wondering whether or not it can do a particular thing, the answer is going to be “yes” or “there’s a package to do that” or “it’s easy to add that functionality” or “I don’t know but Claude could probably get you pretty close in modifying it to do that.” I’ll still take some notes on a paper inside of Emacs, but Zotero is how I primarily manage annotations. When I do a literature review I’ll make a big note in Emacs and just link to the papers that I’m referencing. If you are a plain-text maximalist and like to sync everything via Git or something, then you should be using Emacs. If you are strong enough to resit the pull of closed-format tools for this long, Emacs is for you. It is not a text editor; it is a toolkit to build your ideal text editor . If getting started is intimidating, try out my starter kit , which is basically a set of sane defaults with helpful commentary on how to customize it further. Using Emacs will enable you to build a workflow that is exactly tailored to your idiosyncrasies. It’s an investment, but a worthy one. So, if you are committed to the Emacs + plain text way, here is what I would recommend: Still use Zotero to store papers & associated metadata. Don’t use it for annotations though. Use Emacs and install the Citar package. It ships with a function called which can help you jump from Emacs → Zotero entry. I use this a lot. Use the Denote Zettelkasten-style personal knowledge management (PKM) system. This provides utilities to create notes with tags, links (and automatic backlinks!), etc. all in plain-text. Sync this with Git or whatever. Tie Denote and Citar together with the denote-citar package. Now, when you search for a paper with Citar, you can open a notes file for that paper. When you do, you’ll get a split screen: paper on the right, notes file on the left. If you use the pdf-tools package (and you should) then you can even add annotations to the PDF inside of Emacs! The most important thing is that you build your own system. You have to own it. You might find it easier to adopt someone else’s system, but you should be intentional about every habit you acquire. Be prepared to iterate. I used to be rather rigid with how I organized papers. I found that extreme structure was more constricting than helpful, so there’s a little messiness with how I’m organized, and I’m OK with that. If you want to know exactly how I configure any of the above packages in Emacs, feel free to contact me . Still use Zotero to store papers & associated metadata. Don’t use it for annotations though. Use Emacs and install the Citar package. It ships with a function called which can help you jump from Emacs → Zotero entry. I use this a lot. Use the Denote Zettelkasten-style personal knowledge management (PKM) system. This provides utilities to create notes with tags, links (and automatic backlinks!), etc. all in plain-text. Sync this with Git or whatever. Tie Denote and Citar together with the denote-citar package. Now, when you search for a paper with Citar, you can open a notes file for that paper. When you do, you’ll get a split screen: paper on the right, notes file on the left. If you use the pdf-tools package (and you should) then you can even add annotations to the PDF inside of Emacs!

0 views
Lambda Land 1 months ago

Getting Started With Lock Picking

There are few sounds as satisfying to me as a lock popping open, especially when it’s a well-machined lock that makes a nice, crisp “chink!” sound as the shackle releases. This post is some suggestions I gave to a friend of mine who wanted to get started in lock picking. Please consult your local laws to know the ins and outs around the hobby. Nothing here is legal advice. Also, while I do link to a few product pages, I never have and never will take commissions for material on this blog. I am recommending these tools because I like them, and not for any financial gain. Rules for Lock Picking There are two big rules to follow when picking locks: Rule 1 should be fairly obvious: picking other locks can be a criminal offense. Rule 2 is for your sake: picking a lock can, on rare occasions, break the lock. If the lock is in use, you might have to get bolt cutters or a big drill to fix the problem destructively. Tools matter. The biggest piece of advice is this: never buy picks off Amazon! Those pick sets are made with cheap steel that gets caught in keyways and provides lousy feedback. I thought I was a bad picker for many years—and that is true—but it was also due in large part to how my tools were holding me back. Remember: a few high-quality tools is better than a big set of nearly useless ones. Get picks from JimyLongs . They are the best. They will last you forever. Everyone on r/lockpicking loves them. They are remarkably affordable. They’re versatile and comfortable. I would start with the Basics and Intermediate sets. They’re both 0.019" thick, which is a touch thinner than the picks you were working with (0.025") which means feedback is a little more subtle, but it’s easier to get into more keyways. The two sets will also get you a nice selection of turning tools. The z-bar TOK (Top Of Keyway) turners are really nice to use; I almost exclusively use TOK turners now. I love the Covert Instruments ergo turners. They make practicing so comfortable. Left-handed pickers: the ergo turners will probably not work for you—sorry! They might go on sale around Black Friday, so watch out for that. I wouldn’t bother much with other stuff from CI unless you see something that you really like. I do have the Covert Companion with some extension kits for EDC-style (Every Day Cary) portability, but it’s rather terrible for practice. A great resource is the LPU Belt Explorer . This is a big categorization of pretty much any lock you’ll ever run into broken down by complexity. For example, here is the entry for the Master Lock 140 —a great beginner lock. The locks are organized according to “belts” like those in Karate. You can find Brinks laminated and brass padlocks at Walmart. These are both in the yellow belt tier and are fine locks for getting started. Once you start wanting better locks, I have found that locks from Abus tend to be well-machined and give good feedback. Sometimes a crappy Master Lock can be hard to open because it’s greasy or something on the inside and that can totally kill feedback. A dirty old lock is typically harder to open than a clean, well-machined higher-tier lock. Not having feedback when picking a lock is like trying to assemble a puzzle while blindfolded. If you get a used lock, you might want to clean it first to get that feedback back. As you go higher, the Abus 55/40 is a fantastic orange belt. The American 1100 is a popular green belt. I just bought 7 of these things off of Ebay. The Master Lock LOTO lock (plastic body; lots of colors) is another excellent green belt that will force you to get a feel for spool pins. That’s as high as I’ve gone. Around Black Friday definitely check out the Covert Instruments practice lock: it’s re-pinable and comes with a bunch of pins so you can practice getting a feel for different bitting and security pins. My brother got me this set with the set of picks I let you borrow for Christmas, and the lock has been super helpful. If you like Reddit, r/lockpicking is a great place to hang out, ask questions, and get tips. Lots of good YouTubers: Start with these picks and locks: Level up with these: Decide where to go from there! Only pick locks that you own. Never pick locks that are in use. https://jimylongs.com/products https://covertinstruments.com/products/ergo-turner-set Lockpicking Lawyer (of course): He focuses more on picking commercial locks and pointing out their security flaws. Not necessarily the best resource for learning lock picking. (His “inside perspective” series is great though!) Lockpicking Dev: This channels has a little bit more about how to actually pick locks. Lady Locks: She had a video that helped me get my first American 1100 open. Basics set from JimyLongs lock picks Master Lock 140, Brinks laminated Intermediate set from JimyLongs Covert Instruments ergo-turners Locks from Abus, ACE hardware (their brass padlock is insane —I have not yet opened it)

0 views
Lambda Land 2 months ago

AI stands for “Artificial Inanity”

There’s something icky about LLM-generated text when you think it’s written by a human. I think I finally put my finger on one reason why I feel this way. Note on the title: “Artificial Inanity” comes from Neal Stephenson’s novel Anathem . At work I was sent a long design document and asked for my thoughts on it. As I read, I had a really hard time following it. Eventually I guessed correctly (confirmed via a follow-up conversation I had with the “author” I have “author” in quotes because, if a machine wrote it, you don’t merit being called the author of the work. ) that an LLM had generated the majority of the document. Parts of it sounded like a decent design document, but there was just way too much fluff that served only to confuse me. When I read technical documents, I read to understand the content. In this mode of reading, I operate under the assumption that the author had a reason for choosing the words they did, and that every sentence is there to convey something that the author wishes me to understand. This mode fails when an LLM or the like has generated the text. When I read something I know came out of a computer’s probabilistic sampling of a token-space, I have read knowing that every statement might be some hallucinated slop or incidental filler. I cannot trust that the human operator’s intent is expressed by the machine. In fact, I am confident that it is often not , but I have to waste tremendous effort trying to find that gap. Reading slop text when I think I’m reading real text is exhausting: since I am not on the alert for hallucinations or irrelevancies, every turn of phrase that seems out of place causes me to wonder why that phrase it there and what am I missing when in reality, such questions are ill formed: that was just a phrase composed by accident that sounds good but actually is devoid of much intent at all. Intent is the core thing: the lack of intent is what makes reading AI-slop so revolting. There needs to be a human intent—human will and human care—behind everything that is demanded of our care and attention. Even if you agree with Rolland Barthes’ The author of The Death of the Author , an essay where Barthes argues that focusing on the author’s intent is fruitless—the meaning of a text is the effect it has on the audience. views on literary criticism, the fact that there is an author who put care and intent into a work imbues that work with infinitely more meaning than if it were spat out by a machine. Counterfeits to human connection will—unfortunately—always be in demand. The multi-billion dollar industry churning out pornography is proof enough. People will probably always, from here on out, be using LLMs to cheat their way through classes and themselves out of learning. Some might turn to them for some faux-companionship. Others will be prompting themselves to death by offloading more and more of their reasoning to machines, convinced that the computer—like a slot machine—somehow will let them win bigger in life. I am not saying that LLMs are worthless—they are marvels of engineering and can solve some particularly thorny problems that have confounded us for decades. But it’s important to remember that, no matter how capable these machines get, they are not humans. And no human is so worthless as to be replaceable with a machine.

0 views
Lambda Land 3 months ago

Bedrock Version 1.5.0 Released

I just released version 1.5.0 of Emacs Bedrock —a super minimal starter kit for Emacs. This is a minor change: I’ve fixed a few bugs and added a package or two to some of the optional config files under . Unlike some starter kits, Bedrock isn’t meant to be updated in any systematic way: you copy Bedrock’s config files once, and then you modify them to your liking. Why? Bedrock is meant to be a starting point for users to build their own Emacs config. It’s meant to encourage discovery and personal customization, and it does this by not hiding anything behind any “magic” complexity. I don’t want to discount the hard work done by maintainers of frameworks like Doom Emacs and the like—those have their place and they work for many people. But for myself and many others that I’ve helped, frameworks like those are too much. Better to have a lightweight foundation on which to build a tailored Emacs setup. If you want to update an existing Bedrock-based config, the best way to do this is to just look at the diff between 1.5.0 and the commit you cloned from and pick out the changes you want to incorporate. It shouldn’t be difficult: Bedrock changes relatively slowly, so there shouldn’t be a lot for you to modify. There are a few bug fixes too minor to detail here. Besides those, the biggest changes are: Emacs 31 is currently in development, and I’ve got my eye on a few changes specifically that I’d like to incorporate into Bedrock. I’m tracking changes to Emacs 31 at issue #44 on Codeberg ; feel free to join the conversation there if you find something in Emacs 31 you think would be good for Bedrock. Major exiting things: I’ll keep pursuing the NEWS file for this release. The Emacs contributors have been really knocking it out of the park the past several years with all the goodies we’ve gotten like better JSON parsing, including Eglot, smooth scrolling, native compilation, and so much more. Besides the Emacs 31-centric updates, I hope to add some config that will make it easier to customize a Bedrock-based setup without modifying the core files. While I believe modifying the core files yourself is the best way to get the most out of Bedrock, I am not opposed to accommodating those who would rather add modifications on top of Bedrock than tinkering with the core files instead. Tracking this in issue #13 on Codeberg . Thank you to all who have tried out Bedrock and a special thanks to all who have sent me feedback. Happy hacking! Added some sample configuration for the TempEL package in . It’s another package by Daniel Mendler, and I’ve enjoyed its lightweight templates. I personally find it a lot easier to use than similar packages like yasnippet. If you’re running Emacs 30, is now enabled to make soft text wrapping on things like bulleted lists (like this one!) line up the beginnings of the lines. Another thing for Emacs 30 users: now turns on to give you a visual indication of what project you’re in. Emacs 31 supports child frames in TTYs, so we shouldn’t need the package any more. I love it when we can remove packages without sacrificing functionality! Tree-Sitter support is still bumpy; Emacs 31 has a config option which hopefully will solve some headaches.

0 views
Lambda Land 3 months ago

How I Take Notes for Research

The key principle I follow is this: how I take my notes will evolve over time . I do not stick to any system too dogmatically. That said, I’ve settled on a system that’s been fairly robust and stable for the past few years. I have tweaked it here and there to make it easier for me to find what I need. I make heavy use of org-mode and linking. To make linking as ergonomic as possible, I use the following functions and keep them bound in my global keymap so I can access them in any mode: This will create a link to whatever your cursor is on and store it for later insertion. It will work really hard to get you a link right back to where you were. Sometimes I use this to create a link to a location in source code to come back to later. This inserts a previously stored link. Visit a link—no matter the mode. In I can simply use to follow a link, but having this bound globally means I could insert a link inside e.g. a comment in some source code and then visit its location with this function. This is like a history-back button: once you visit a link, use this function to go back to where you were. I use Denote and the denote-org and denote-sequence extensions for all my notes. Below is a sample of my configuration: The highest-level entity in my research is an “epoch”—this is a topic that spans multiple papers. The current epoch I am working on is for choreographic programming. My previous epoch was type tailoring, and the one before that was floating-point work. Epochs start a new note sequence. These are the “folgezettel” notes that Denote facilities creating. is the first epoch that I started doing this with; it’s signature is . The next epoch will get the signature , and so forth. Each epoch has, as children, notes for ideas related to that epoch, Paper files , and a “daily notes index” file, the sole purpose of which is to serve as the parent for scores of Daily note files . An epoch note defines a keyword for that epoch and has the following headings: List of links to any software projects that result as a byproduct of the research. List of links to paper files that fall under this epoch. Current task list Most tasks live in daily note files , but sometimes I’ll put items here. Idea backlog List of off-the-cuff ideas that are too small to fit into a full note, but might go in a note later on. I also like keeping a dynamic org block to insert backlinks. I do this in epoch and paper files. Here is a (truncated) example of what an epoch file is: Paper files organize everything about a paper . These are children of an epoch. So, the first paper I write about choreographies will have the sequence . Headings in a paper file: Primary links Include a link to the epoch the paper belongs to, as well as to the project folder where the LaTeX source of the paper lives. Key information Conference deadlines, etc. Contributions Keep track of what this paper is trying to accomplish. Things to include in paper If I come across something that needs to be cited, mentioned, or dealt with, it goes here. Works referenced If I find something when I’m not in a state to put it in the bibliography, it goes here. A list of items for the paper. I create one of these a day through the function. Then I have some templates that I expand with the tempel package. Here’s what my templates look like: I create the journal entry, expand the template, then fill out the form. I make sure to “adopt” the note as a child of the “daily note index file” for the current epoch. This file is basically empty and just serves to ensure that the sequence space underneath an epoch doesn’t get cluttered with all these day-to-day task notes. The daily notes are just there to track what I did that day . They are lab notes. If I have something more substantial to say, it goes into its own separate note file, and then I add a link. Sometimes I’ll have a thought about an epoch that doesn’t fit in my current paper. When that happens, I just create a new child node of the epoch note. I have lots of notes under the sequence that are not paper files. I have some utilities to go through all of my files tagged or and produce an org-agenda view. Note: generating the agenda takes on the order of several seconds as it looks through a lot of notes. Below is roughly how I do it: The uses to find files with the desired tags in the portion of a file. Note that my configuration for automatically includes the tag for any journal entry I create. I use Zotero to manage my papers. I pay for cloud storage so I can sync my library and annotations between my desktop and my iPad. I love using my iPad to mark up papers. (There’s now an Android app for Zotero.) I use the Citar package to read the BibTeX database Zotero creates with the Better BibTeX file. I use this to quickly insert citation keys. Citar is smart enough to read your LaTeX files, find the directive, and read that BibTeX database when you’re working on that file. Citar is most useful when paired with Vertico . There are ways to write notes about papers via Denote and Citar; however I typically do all my note writing about papers with a stylus on my iPad. Other, bigger notes that are a product of my thoughts go either in my analog notebook or in a regular note file. This will create a link to whatever your cursor is on and store it for later insertion. It will work really hard to get you a link right back to where you were. Sometimes I use this to create a link to a location in source code to come back to later. This inserts a previously stored link. Visit a link—no matter the mode. In I can simply use to follow a link, but having this bound globally means I could insert a link inside e.g. a comment in some source code and then visit its location with this function. This is like a history-back button: once you visit a link, use this function to go back to where you were. Products List of links to any software projects that result as a byproduct of the research. Papers List of links to paper files that fall under this epoch. Current task list Most tasks live in daily note files , but sometimes I’ll put items here. Idea backlog List of off-the-cuff ideas that are too small to fit into a full note, but might go in a note later on. Primary links Include a link to the epoch the paper belongs to, as well as to the project folder where the LaTeX source of the paper lives. Key information Conference deadlines, etc. Contributions Keep track of what this paper is trying to accomplish. Things to include in paper If I come across something that needs to be cited, mentioned, or dealt with, it goes here. Works referenced If I find something when I’m not in a state to put it in the bibliography, it goes here. Tasks A list of items for the paper.

0 views
Lambda Land 3 months ago

Programmers and Their Monospace Blogs

Many developers seem to have a fanatic obsession with monospace fonts and using them to make their blogs look “cool”. I won’t call out anyone’s blog specifically, but you don’t have to look to hard to find some. As an example theme using a monospace font by default, look at hugo-theme-terminal , which has over 2,400 stars on GitHub. If you have a blog or are thinking about starting one, and you are writing mostly prose (you probably are), I have one suggestion for you about fonts: Do not use monospace fonts for prose. Please use a nice proportionally-spaced font instead. It will be nicer for your readers. I assume you care about your readers. Maybe there’s a slim fraction of a percent of developer/writers out there who are writing just so that they have a big portfolio of “content” with some metrics they can use to show off or brag about on LinkedIn. Whatever—those blokes probably don’t read much anyway, so I think I’m safe to ignore them. Even worse are the people who use an LLM to generate content for LinkedIn. Like, how banal can you get? Also, note that I’m using the word content in a slightly pejorative sense: LinkedIn addicts will talk about content as just some stuff that you need to generate to fill a space. It’s substance that matters. I want to read something that is trying to say something—not something that’s just taking up space on a (web)page. If you care about your readers, you should make the reading experience pleasant for them. If I open a website that makes it hard or is uncomfortable for me to read/scan/whatever, my patience for reading whatever is on that site drops and I close the page. What things make a website uncomfortable to read? Here are a few: Setting light-gray text on a slightly-less-gray background is a great way to make people squint at your site in frustration. Not centering your text and/or having lines that are way too long also infuriates me. I want to read your article centered in my field if vision where I don’t have to turn my head. I’ve got a big monitor. Just slap and on the element in your CSS and you’re good to go. It’s not hard. Monospace fonts are a bad choice for prose because—news flash—we’re not used to reading monospace fonts! Every book on my shelf—from deeply technical texts like Types and Programming Languages by Pierce to high fantasy like The Way of Kings by Sanderson—is set in a proportional font. Monospace fonts are a holdover from the typewriter age. Thankfully, our technology is well past the limitations of that machine, and good typography can rule again. If you still don’t believe me that monospace fonts are bad for prose, maybe professional typographer-cum-programmer Matthew Butterick can change your mind . If you look around on my blog, you will find plenty of code set in a monospace font . Code should be set in a monospace font. I actually use a monospace font when I write! (Partly because I’m used to it, and partly because I haven’t bothered to set up my editor to switch to something else when I’m writing.) So please don’t take this post as a tirade against monospace fonts in all contexts. Yeah, I do use my browser’s reader mode frequently when I want to read some long-form text. It’s a nice way to get a decent-looking view of the text on a page. In some cases, it hides distracting elements, making it infinitely superior to an ad-riddled page. But if reader mode is your suggestion to get around your bad monospaced font, isn’t that an admission of failure? I would think that you would want your blog to be nice enough to look at that people don’t even think about using reader mode because your website is pleasing to read as-is. If you want to include source code and images into your post, reader mode sometimes mangles those. I won’t advise you exactly what style you should set your blog/website in. That’s up to you and is in large part a matter of taste. But this part is not subjective: prose is meant to be set in a proportional font. Please stop using monospace for prose. Poor contrast Bad margins/too-long lines Crappy fonts

0 views
Lambda Land 5 months ago

Real Programmers

There’s been an explosion of tools for software development. At the same time there’s a growing sense that software quality isn’t what it used to be—or that developers these days don’t understand what it takes to be a “real” programmer, whatever that means. I’m not that old, but I have some old-school tool preferences. Some tools I really like; in other cases I feel that by not adopting particular habits, I’ve gained or retained an edge over others in the software development space. Notice: This is a rough-and-raw dump of some ideas that were ratting around in my head. Read at your own risk. I learned programming from a command line with nothing but a few books and some pages to guide me. I am extremely comfortable navigating and controlling my computer via the terminal. Most college kids these days will start their programming journey with an IDE and Python Or C++ if they’re unlucky. and they learn nothing about how the operating system works under the hood. This is why things like MIT’s course “The Missing Semester of Your CS Education” exist: to get programmers up to speed on the tooling developed over the past several decades. The thing is, there are a lot of new-fangled tools that programers have been picking up: instead of Emacs or Vim they use VS Code or Cursor. Instead of the CLI they use the VS Code Git client. Instead of and they… I don’t know what they do to replace those tools honestly. Maybe they look for an NPM package that purports to do what they want. A lot of the new tools have less friction than old tools. But that typically comes at the cost of being further away from the underlying infrastructure. Tools like VS Code, etc. are less inspectable and understandable than Emacs and the like. GUIs—sometimes labeled “point-and-grunt interfaces”—almost always expose a smaller set of operations than comparable text-based interfaces to the user. Command line tools win by being composable; GUIs are very hard to plug together quickly in an ad-hoc way. I believe it is important that every serious user of a computer—CS students especially—get familiar and comfortable with the command line. Our computers are rickety contraptions that frequently break or have novel requests made of them. Knowing the constituent components that make up an operating system, as well as some of the fundamental tools around software development (e.g. Make, Git, etc.) can help you get used to gluing generic utilities together to create perfectly-tailored workflows or one-off tools. I have been able to perform acts of astonishing wizardry—relative to the “average” computer-user—because I knew how to glue together a few command line utilities to process a huge amount of data in a short amount of time. Likewise, I am a capable programmer and have been able to effect some extraordinary refactors because I know how to make Emacs do my bidding in ways that would make your average VS Coder weep. Big respect out there for all the hard-core Vim users too. Vim is the gold standard for text editing efficiency. Being able to “pop the hood” on your computer and figure out what is going wrong lets you build more, better. When something goes wrong, you have a better mental model of how to fix it. When some new task comes up that no developer has ever encountered—it happens more than you think—you have the tools you need to create a novel solution. And yet, I do not know how my car works. I drive an automatic. Computers are becoming increasingly a commodity like a car—is it so bad that more people don’t know how the innards work? Two points: first, I believe that it is good that most people do not have to know how a file system functions to get work done on the computer. Second, I believe that any craftsperson understand their tools to the fullest extent—for race car drivers, this would be understanding how an engine works, and for programmers, the operating system. To the first point: most people have high-level tasks that they want to accomplish. They want a friend to see a picture that they took, so they load it up in an email or messaging service and send it off. They don’t need to know about image encoding, network routing, etc. to get this done. This is good . Programmers, too, often have high-level things that they want to accomplish. Consider editing a code base: when reading some code, I often want to jump from where a function is called to where it is defined. This task is more semantically meaningful to me than carefully crafting a regex for to show me where I might want to go. The less friction in this process, the better: it lets me stay more in flow and helps me get my work done. The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise. Edsger Dijkstra I learned how to use Git via the CLI. Now I use Magit . This is because Magit walks a delicate line: it makes common operations much easier than the CLI, yet at the same time it doesn’t baby me—it actually helps with discoverability—and it can compose Git commands at times to accomplish higher-level functions. Example: there are commands to “spin off” a series of unpushed commits into a new branch. It does this by creating a new branch, then reverting the old branch to point to whatever its upstream remote was. Super helpful when you start development work and realize after the fact that you should probably be working on a new branch. Moreover, I can see how it works under the hood: Magit can show you all the commands it’s running to do what you want. I think we need more tools like Magit: things like lazygit , lazydocker , tldr , LSP, etc. all extend users’ understanding of systems whilst not keeping them too far from the source. I think it’s valuable to be able to choose the level of abstraction to work on. I’m never far away from a command prompt while I’m working on my computer. Yet, most of the time I do my Git work via Magit. Magit is powerful enough that I typically don’t need to resort to the command line, yet sometimes I do for things like setting configuration variables. I think that it is good that we have tools that operate at higher levels of abstraction—GUIs even—because these can help us stay more rooted in the semantics of our problem domains. At the same time, I think it is crucial that software engineers get comfortable with understanding the fundamentals of operating systems, version control, text editing, scripting, etc. so that they can build new abstractions when the need arises. Computers are still in their infancy. They’re just barely taking their first steps. I see no reason to use exclusively decades-old command line tools when we have some fantastic new utilities that aid our ability to build more and better software. Yet we must not forget the basics, lest we loose the ability to take steps on our own.

0 views
Lambda Land 5 months ago

TV Shows for Kids

When I was in my early 20s, I vowed that I would keep my kids from watching any amount of television. Turns out, sometimes you really need a break as a parent. A good show can keep your kid entertained while you perform necessary tasks like preparing a meal, doing the dishes, or getting just enough extra sleep to not blow your top or doze off in the car while you drive your kid to preschool. So, I have had a change of heart: TV can be a tool, but not all TV programs are created equal. Without further ado, here is my tier list of the shows I’ve seen or heard about: These are the shows that I am fine with my kid watching any time. They are well-written, low-stimulus, and never get annoying. Why do I care so much about low-stimulus shows? I don’t want my kids getting hooked on dopamine rushes. I’d rather that they play imaginatively as much as possible. Low-stimulus shows help by not desensitizing kids to the gentler kind of happiness that comes through creative play. How could it not be Bluey ?! It’s a low-stimulus show about parenting that kids happen to enjoy as well. The dad, Bandit, is an enthusiastic, clever, engaged parent who sometimes messes up but always makes up for it. The mum, Chili, is loving, firm, hard-working, and creative. The relationships are positive and realistic. My favorite episodes are: There are more. Bluey deserves all the hype it gets. It’s that good. If you have a toddler, watch Bluey . This feels like an Irish-flavored Bluey -type show, but with Irish-accented puffins. Sweet show with a pretty animation style. Most episodes are just about the main character, Oona, exploring the island. Less anthropomorphic than Bluey . Good shows that don’t quite rise to the level of Bluey and aren’t as visually beautiful as Puffin Rock but are still fun and occasionally educational. Four kids fly around in a “Rocket”. Each episode features a work of classical music and some art by a famous artist. The kids never fight—the whole show is about them solving problems. The best part is that my kid can now recognize lots of different important classical pieces and enjoys listening to them. Occasionally the episodes get a little annoying because of how formulaic they are, but maybe that’s good for the kids. I grew up watching Blue’s Clues and it’s still such a nice, sweet show. These are shows that we will turn on if we have to. I wouldn’t consider them bad , but they are moderately annoying. This is a TV show based off of the series of children’s books by Laura Numeroff and Felicia Bond. The show is… fine. Most of the characters seem to have a sense of helplessness when something gets lost/broken and they feel that the circumstances “…will be ruined—forever!" This is a phrase that I am pretty sure crops up in every episode. Ugh. At least half the episodes involve some MacGuffin rolling down a hill to a pond. Again, it’s not a bad show, but sometimes my daughter will start talking like Mouse with one-word requests for things like “thirsty” or “hungry” instead of speaking in full sentences. These are shows that I don’t consider actively harmful, but I strongly dislike because of how annoying they are or because my kid picks up bad behaviors from them. On the surface, this is the perfect show: it’s a spin-off of Mr. Roger’s Neighborhood , the animation gentle and low-stimulus, and it’s moderately cute. But oh—oh how deceptive it is. Daniel Tiger displays an impressive degree of learned helplessness and timidity. All of the “problems” that he encounters in the show are invented and stupid. E.g., it is raining outside so we can’t play on the beach—grrr I’m mad and now I need help calming down from a total meltdown. The worst thing from this rainy-beach episode is when the kids drag in several wheelbarrows’ worth of sand onto the living room carpet and, when the mom comes in and gets angry, Daniel tells the mom to take a deep breath and calm down from her slightly agitated state. If my kid ever dragged several cubic meters of sand into any part of my house, I reserve the right to be upset. Anyway, cute on the surface, aggravating underneath. I have not watched these shows. I’m too scared to go near them with a stick. CoComelon is the epitome of high-stimulus children’s programming. In every shot the camera is panning, no shot lasts more than 3 seconds, and the show’s developers utilize a tool they call “The Distraction” to determine when scenes are insufficiently attention-grabbing: when a test subject (a small child) looks away from the show to look at a screen showing adults doing banal household chores, the animators will amp up the show at that point to keep kids dialed in. I would rather not have my child’s dopamine receptors burned out by stimulus-overload. Look, if you like CoComelon , I won’t judge you. If you’re wondering if you should pull it up for your kids, I would stay far away . Kids need to be bored. The more bored they are, the more time they have to be creative and develop an internal world. I do think it’s fine to have some TV—I grew up loving Arthur , Cyber Chase , and Reading Rainbow . It is really nice to have half an hour to shower, eat, and get some chores done so I can better take care of my child. I’m trying to find good shows though. I hope this helps any parents out there looking for ideas. :) Hang in there—raising kids is the very best experience this world has to offer.

0 views
Lambda Land 8 months ago

A Quick Guide to LaTeX

LaTeX is a powerful typesetting system hamstrung by a few decades-old decisions and some… ahem… questionable design decisions. Nevertheless, its ability to typeset technical documents remains unmatched, and it enjoys wide support across STEM fields. Learning LaTeX is a worthy use of your time, if you intend to pursue a career in science. This is meant as a short and simple how-to guide for learning LaTeX. It is not meant to be comprehensive, but rather serve as a guide of where to look to get the information you need to know. It is organized as a problem → solution mapping. These are the places where I learned LaTeX. They will serve you well. There are several LaTeX distributions. The only one I have ever used is the TeX Live distribution, and I’ve never needed anything else. The TeX Live distribution comes bundled with most, if not all, of the packages you will ever need. So, when I suggest using the e.g. or package, you should be able to just put in your preamble without worrying about downloading anything. Further reading: LaTeX Wiki: Document Structure LaTeX documents have a preamble and then a document body . Roughly, the preamble specifies the look of the document through setting variables and importing libraries, and the document body holds all of the text. LaTeX documents have this format: A document class header is the first line in the file, and it describes what kind of a document you are making. Most common form is . Technical journals typically define their own document class, which is kind of like enforcing a standard style sheet out-of-the-box. Document classes typically have options to configure fine points of the document setup. A standard article class might look like this: This says that the document is an (other types are , for slides, and more), and the options mean the PDF should size to a US letter sheet, and will highlight hyphenation errors for you. There are lots of options for each document class; consult the documentation as needed. Put this in your preamble: This will let you put UTF-8 characters inside your document file. You want to use the package. For example, to set the left and right margins to 1.8 inches, and the top and bottom margins to 1 inch, do this: The default paragraph style is to not indent the first paragraph in a section This is correct typographic practice, so don’t mess with that. and to indent every subsequent paragraph. If you want to change this so that paragraphs have no indentation but have a bigger separation between them (my personal favorite paragraph style) then you can put something like this in your preamble: The length controls how much to indent a paragraph. By setting it to we turn off paragraph indentation. The controls how much space to put in between paragraphs. I think its default value is . By setting it to half of we add about a line and a half of blank space between paragraphs. (See LaTeX Wiki: Lengths for more details.) You need the package. Here is what I do to set the main font to Valkyrie, the sans-serif font to Concourse, and the monospace font to Iosevka: As you can see, there are quite a few options enabled for each of those. The option for Valkyrie means that the file I want LaTeX to use for bolded Valkyrie is at . If the file were named I would have to say . You need to use LuaLaTeX to use the package! This should come with your TeX Live distribution. I will typically say to build. Take a look at the LaTeX wiki and this Overleaf tutorial . LaTeX gives you a lot of control over the presentation of tables. Further reading: LaTeX Wiki: Source Code Listings Putting source code in LaTeX is a pain. Lower your expectations now and life will be easier. First, put this in your preamble: The first two lines bring in the and the packages. The first lets you define code blocks, and the second lets you define custom colors, though some named colors (like “magenta” in this example) come built-in. The lets you create something kind of like a stylesheet for your code. The most important big is setting the option. This sets your code in a monospace font. (Specifically, it sets it in the font. In the example it also sets it to be a little smaller at .) Inside your document you can put your source code in a environment like this: There are a lot of other ways to customize this package, but I will not cover those here. LaTeX has a lot of packages. Here are some that I like using: Here’s an example LaTeX document that uses a lot of the config above. Hope it helps you get started with LaTeX! Overleaf Looks like Overleaf has a nice getting started guide here .

0 views
Lambda Land 10 months ago

What's New in Emacs: Last Decade Edition

Emacs has come a long way in the past decade. This is meant as a guide to anyone who’s been using stock or near-stock Emacs for some years and wants a quick update on the new shiny stuff that comes bundled with Emacs. This guide assumes you are running Emacs 29, which was released in 2023. Completion is pervasive in Emacs: hit whenever you’re selecting a file ( ) or running a command by name ( ) and the like, and completion kicks in. Emacs’ built-in completion framework and interface have gotten a huge upgrade in recent years. If you hit a bunch of times when writing e.g. a file name, you’ll open up the buffer. Emacs 29 has lots of ways to configure this buffer to be much more useful than the default: Here’s what each one does: That’s for minibuffer completion. Emacs also supports completion for whatever the cursor is on; Emacs calls this “completion-at-point”. Here’s how to get nice tab-complete behavior in Emacs: Setting to means that when you hit , Emacs will first try to indent the current line. If the line is already indented, then Emacs will call the completion-at-point facilities of Emacs. Assuming you have the minibuffer completions set up as explained above, you can use the same interface to complete in the minibuffer as well as tab-completion that you see in other editors. These are just the built-in completion mechanisms. Emacs’ framework has gotten a great upgrade that has allowed packages like Vertico (minibuffer completion) and Corfu (completion-at-point in a popup window) to flourish. Vertico and Corfu are two of my favorite packages . The is neat: it takes a list of different “styles” that can be used when filtering candidates. The is particularly fun: type and you should see the function in the buffer. Emacs 29 added the Eglot (“Emacs polyGLOT”) package to the core distribution: Eglot is a lightweight Language Server Protocol (LSP) client. This lets Emacs talk to language servers like clangd or rust-analyzer (etc.) to get things like good code completion, jump-to-definition, documentation, etc. Eglot is conservative: it doesn’t get in your way, and all the completion smarts come up only when you ask for them. Of course, it’s possible to make it more eager and behave a little more like VS Code, but that’s your choice. I personally use jump-to-definition more than any other feature. If you’re working on—say—a Rust project, install rust-analyzer on your system, then open a file in a Rust project and run . Now all of the completion-at-point mechanisms should be smart about the language you’re working with. You should also be able to jump to e.g. a function definition by putting your cursor on a function and invoking (bound to by default.) Emacs now has package that adds some nice stuff for navigating projects. For example, the command is like but is restricted to files in the current project. This pairs nicely with fancier completing-read interfaces to let you find and jump to files without having to navigate the entire file hierarchy. Tree-sitter is a parser generator tool that is fast and—most importantly—works on incomplete inputs (e.g. a file you’re in the middle of editing). Emacs 29 added support for tree-sitter enabled modes. To be honest, it’s a little clunky still, but support is improving. (The Emacs 30 pre-release is already better than what 29 does.) Tree-sitter is too big for me to cover here; see Mickey Petersen’s excellent article on the subject. There’s a built-in dictionary feature in Emacs 28. Use this to look up words from https://dict.org : Use to look up the word at point. You can of course bind this to a key for more convenience. (Looks like there’s also which shows the definition of a word when you hover over it with your mouse. Fancy, and a bit too much for me. But very cool!) Going really far back is : spellchecking while-you-type. Nothing special about that; what is special is how easy it is to correct words: hit to cycle through corrections of the closest misspelled word. For code, there’s , which does spellchecking in just comments and strings. Even though this has existed for a while, I don’t see it turned on very often. I now use Jinx for my spellchecking needs. It’s faster and more flexible, but it draws a lot of inspiration from . This mode is also a bit older, but I love : turning this on will soft-wrap lines at word boundaries and make all your motion commands behave according to visual rather than logical lines: e.g. if I have a really long line that is wider than my window, it will be wrapped—just like in any word processor—and pressing the arrow keys will move me to the character that is visually above the current one, even though it might be on the same line. Emacs comes with two new themes: (light) and (dark). These themes have excellent contrast and are ment to conform to the highest levels of visual accessibility. Try them with . Emacs has a built-in package manager called . This makes it easy to install 3rd-party packages. Run to activate. I have a list of my favorite Emacs packages , which include (but are not limited to) Magit , Vertico , and Avy . There are several places you can get packages from; when came out it only supported packages from GNU ELPA . Recently Non-GNU ELPA got added to the stock list which opens up a huge set of packages such as the venerable Magit package. And of course, there’s MELPA , which you have to add to your config yourself if you want packages from there: Emacs 28 added the awesome macro, which makes configuring packages really nice. Previously you might have written something like this to configure a package: Now you can do this with a macro: What you don’t see is all the extra work that does behind the scenes to speed up startup times by lazily loading your package. There are lots of options for setting up hooks, different kinds of configuration, keymaps, etc. Emacs 27 added a native JSON parsing library. This meant that things like Eglot (and other LSP clients) got much snappier. Emacs 28 can compile Emacs Lisp code to native byte code. This means that all Emacs packages just run faster. There’s a lot more, but those are some of the biggest ones. Nice to see Emacs getting a lot of love. Many of the improvements to the core have also lead to a flourishing of excellent third-party packages. Here are some of my favorite: There are so many more packages that have come out in the past few years that I use all day every day. I wrote about some of them here . I made Emacs Bedrock as a starter kit to help explore all these nice new built-in features of Emacs. It’s a true starter kit: you download it once, then tweak it to your liking. By default it installs only one package ( ; Emacs 30 will have this package built-in) and the rest is tweaking some settings to be what I think the defaults should be. It’s meant to encourage exploration. If you liked something from this post, take a look at Bedrock and maybe you’ll find something else there that you like!

0 views
Lambda Land 10 months ago

Should Programming Languages be Safe or Powerful?

Should a programming language be powerful and let a programmer do a lot, or should it be safe and protect the programmer from bad mistakes? Contrary to what the title insinuates, these are not diametrically opposed attributes. Nevertheless, this is the mindset that underlies notions such as, “macros, manual memory management, etc. are power tools—they’re not supposed to be safe.” If safety and power are not necessarily opposed, why does this notion persist? The problem—I think—is that historically you did have to trade safety for certain kinds of power: if you wanted to write a high-performance device driver, C—with all its unsafe behavior—was your only option. This founded the idea that the “power tools” of the industry were fundamentally dangerous. There’s a few things wrong with this though: Power is relative to the domain of interest. Both Haskell and C are powerful, but in completely different ways. So, when judging whether an aspect of a language is powerful or not, consider its application. Expressive languages get you power without sacrificing safety. New advances in programming language research have found ways to express problem domains more precisely. This means that we have less and less reason to breach safety and reach into the unsafe implementation details to get our work done. It’s good to add safety to power tools. A safe power tool is more trustworthy than an unsafe one. This holds for real-world tools: I will never use a table saw without a functioning saw stop. Specifically in the case of macros, there’s been an evolution from powerful-but-unsafe procedural macros in Lisp to safe-but-less-powerful pattern macros in Scheme, and finally to powerful-and-safe macros in Racket. More safety means higher reliability—something that everyone wants. And with advances in making languages more expressive, you can have a language perfectly suited to a particular domain without sacrificing safety. A language that lets you do more of what you want to do is more powerful than a language where you can’t do what you want. But what does “what you want to do” encompass? If you want to write device drivers, then C is great for you. However, C is not as expressive in some of the ways that, say, Haskell is. For example, in Haskell, I can write lazy, recursive definitions. Here’s a list of all Yes, all the Fibonacci numbers. Haskell is lazy; this will compute as many as you ask for. the Fibonacci numbers: Before you tell me that that’s just a useless cute trick, I actually had to use this when I was building the balancing algorithm in my rope data structure for my text editor written in Haskell . Haskell is incredibly powerful in an expressive sense: a single line of code can elegantly capture a complicated computation. The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise. Edsgar Dijkstra Power is closely related to the domain of interest: a language is powerful in a particular realm of problems. C is powerful for working with memory directly. Conversely, Haskell or Racket is more powerful than C in pretty much every other domain because these languages give the user tremendous ability to match the program to the domain . This is a meta-power that sets high-level languages apart from lower-level ones. Safe languages can be just as powerful as their unsafe counterparts—in many cases, they are more powerful because the abstractions they create better fit the domain. Whenever a tradeoff between power and safety must be made, that is a sign that the language is not the right fit for the domain. Consider how immutability gives you local reasoning power . At one of my industry jobs, our codebase was a mixture of Ruby and Elixir. Both are safe languages, but Elixir is immutable. When I was working on some Elixir code, I could read: and I didn’t have to worry about getting modified in the call to . To understand the output of this function, I didn’t have to worry too much about the implementation of . In contrast, if you did the same sort of thing in Ruby: the method could do something sneaky like set name to if it didn’t exist. You might think, “well, just document that behavior.” Now I need to read the documentation of every function I encounter—I might as well go read the code to be sure the documentation isn’t out of date. Local reasoning means to understand what is passed, I don’t have to worry in the first place if will do somethig to the result of . In this case, I did have to understand what every method call did to understand the function. This made it harder to track down errors because I had to account for all the side effects that could happen at every method call. Certain things like immutability might seem constraining, but constraints can liberate you by allowing you to rely on particular behaviors. Elixir doesn’t let you modify things in-place, but you can rely on this, which makes understanding and composing code easier. Haskell forces you to express side-effects in the type system, but this lets you know that calling a function with a signature like won’t do any IO or throw an exception. Rust doesn’t have like in Java, but you know when you get a pointer, you can safely dereference it and you don’t have to do all the null checking that you have to do in Java. The evolution of syntax macros in Lisp, Scheme, and Racket provide an interesting real-world instance of how safety and power can start off as a trade-off, but with better language design, become complimentary. I don’t have the space here to do a deep dive into Lisp macros, but here’s the short of it: Lisp macros are just functions that receive code as data. This code is represented as nested lists of symbols. All a macro needs to do is return a new list of symbols that will be spliced right into the call site. The problem with this is that these macros are unhygienic : if I introduce a new variable, as I did with in , that is just a bare symbol that can be inadvertently captured producing unexpected output: This is very bad! To use a macro safely, you need to be sure that it’s not introducing variables that you might accidentally capture. Lisp provides a mechanism Lisp has a function called which makes a fresh symbol for you to use. Some other languages such as Julia have a function; is a poor substitute for proper hygiene. to avoid some of the pitfalls with variable capture, but that’s not the end of the danger. If I have a macro that expands to a call to a function, e.g. , I would expect this to be the in scope at the time I defined the macro. However, this might not be the case—a user might inadvertently redefine a function, and then the macro would not behave in the expected way. Scheme has a faculty called , which lets you define transformations between a pattern and a template: Rust’s form is essentially from Scheme, but a little fancier with some syntax classes like and such. This is safe; the examples from the Lisp run as expected: However, we’ve lost some of the power because we can only define transformations between templates. We can’t, for example, write a macro that does some deep inspection of the code and makes decisions on how to expand. Furthermore, there’s no way for us to intentionally break hygiene when we really want to. Racket resolves the dilemma between having to choose between powerful Lisp-like procedural macros, and safe Scheme-like hygienic macros by giving us fully hygienic procedural macros! I have another blog post discussing macros in Lisp, Scheme, and Racket and I go into some detail about the evolution of those macro systems. And if you want to dive deep into macro hygiene, see Matthew Butterick’s excellent explainer on Hygiene from his book Beautiful Racket . The upshot of it is that Racket uses a combination of features (scope sets, syntax objects, etc.) to give the user a richer way of specifying syntax than simple dumb lists of symbols. This avoids inadvertent variable capture as well as keeps function references lined up nicely. However, macros can still do arbitrary computation, which means that we’re not constrained in the way that the pattern-transformation macros in Scheme are. And just to prove that Racket is just as powerful as Common Lisp, here’s the classic macro: This example is inspired by Greg Hendershott’s fabulous tutorial Fear of Macros . The bit lets us introduce new bindings intentionally , whilst still keeping us from accidental breaches of macro hygiene. Consequentially, Racket’s macro system is far more useful than Lisp or Scheme’s systems, and this because of Racket’s safety and expressiveness. You can actually build trustworthy systems on top of Racket’s macro system because you’re not constantly foot-gunning yourself with hygiene malfunctions, and the macros are expressive enough to do some rather complicated things . Safe systems let us build software that is more capable and more reliable. Unsafe power is something to improve, not grudgingly accept—and much less defend as somehow desirable. Languages like Rust and Zig have made systems programming immune to whole hosts of errors by being more expressive than C, and languages like Racket are leading the way in making metaprogramming more useful reliable and less like dark magic. If you want to learn more about writing macros in Racket, check out Beautiful Racket by Matthew Butterick and Fear of Macros by Greg Hendershott. I highly recommend listening Runar Bjarnason’s talk at Scala World, Constraints Liberate, Liberties Constrain , wherein he discusses how constraining one part of a system can open up freedoms of later components that build on that constrained part. Power is relative to the domain of interest. Both Haskell and C are powerful, but in completely different ways. So, when judging whether an aspect of a language is powerful or not, consider its application. Expressive languages get you power without sacrificing safety. New advances in programming language research have found ways to express problem domains more precisely. This means that we have less and less reason to breach safety and reach into the unsafe implementation details to get our work done. It’s good to add safety to power tools. A safe power tool is more trustworthy than an unsafe one. This holds for real-world tools: I will never use a table saw without a functioning saw stop.

0 views
Lambda Land 11 months ago

Towards the Fastest Brainf*** Implementation Ever

In my last post I described how I made a very fast BF interpreter. Well, there’s a lot more speed to be had with an optimizing compiler. This post is a write-up of my assignment for a compilers class, so the post a little rougher than normal. You can find the code at the following places: Files for the compiler: The first role of this module is to define an AST for BF. My AST is represented as Racket structs; these are the nodes that I have: The rest of the module is devoted to optimizations performed on the AST. Initially, only , , , , and cells are present, but through the optimization passes more get added. See § Optimizations for details on all the optimization passes. There is a function, but this just compiles the optimized AST down to Racket closures with a threaded interpreter. The main entry point is the function, which emits some fixed prelude code, the body of the program, and then some fixed postlude code. There are a bunch of functions prefixed with ; these are to help me generate assembly instructions with a particular format. Mostly just wrappers around calls to . The function does the hard work of walking the AST and generating instructions for each node. There’s a loose set of conventions around various registers; the most important is that always holds the address of the start of the tape, and holds the current pointer. I never use these registers for anything else; you can always access the current value under the program head with . There are two special functions for emitting the assembly for nodes: one for forward scans ( ) and one for backward scans ( ). This is just done for organizational convenience. The entry point to optimizing the BF AST is in the function in . This chains optimizations together; each optimization function must take a whole program AST and return a whole program AST functionally. The optimizations are in order they are performed: This pass takes sequences of instructions like , initially represented with and turns it into . Same thing for pointer shift instructions. This cleans up instructions like or that have no effect. This recognizes patterns like , which adds the value of one cell to another. It handles arbitrary shift and add amounts, as long as they’re balanced. Kind of like a baby pass. Handles cases of or to set the cell to 0. Does the basic loop optimization we discussed in class. Easily the most complicated optimization. It abstractly evaluates the body of a loop; it sets the initial pointer to and tracks what additions go to which offsets. Bails out if anything gets too complicated. Optimizes cases of to just set the current cell value to 3. Scan loops, as discussed in class. I precompiled and ran all the files in the folder provided. I noticed that the first run of the resulting binaries took a long time, and that they all ran almost instantly afterwards. I noticed that there was a bunch of network activity; seems like macOS was being zealous in sending signatures for each of the binaries the first time they were run. The benchmarks run quickly, so this added up. The numbers reported here are after running the tests one time to avoid the network penalty. There’s not a whole lot of variance. I ran my compiler on some of the benchmarks from the BF benchmarks suite we’ve been using. I also tried out a few with the BF-interpreter-in-BF. Seems that basic loop optimization is most important for the Hanoi and Mandelbrot benchmarks. I also tried the Sierpinski triangle benchmark but it never ran long enough for me to notice any difference. Contrast with benchmarks running under the interpreter: scan loops are far and away the most important optimization: Gets optimized to this: Any loop with I/O or nested loops—even something like setting something to are not covered by this. Gets optimized to this: The first few lines load up the 0 and stride mask vectors. The only strides I optimize are 1, -1, 2, -2, 4, -4, 8, and -8. At stride 16, you’re only getting one check per loop; might as well just do the basic iteration instead of firing up the vector unit. GitHub repo Codeberg repo This pass takes sequences of instructions like , initially represented with and turns it into . Same thing for pointer shift instructions. This cleans up instructions like or that have no effect. This recognizes patterns like , which adds the value of one cell to another. It handles arbitrary shift and add amounts, as long as they’re balanced. Kind of like a baby pass. Handles cases of or to set the cell to 0. Does the basic loop optimization we discussed in class. Easily the most complicated optimization. It abstractly evaluates the body of a loop; it sets the initial pointer to and tracks what additions go to which offsets. Bails out if anything gets too complicated. Optimizes cases of to just set the current cell value to 3. Scan loops, as discussed in class.

0 views
Lambda Land 1 years ago

How to Make Racket Go (Almost) As Fast As C

I recently wrote about using first-class functions to help make a BF interpreter . This is a follow-up post to describe a nifty solution to a tricky problem that made my program go 2–5× faster and put it about on-par with an interpreter written in pure C. A basic interpreter works by walking down the AST and evaluating nodes recursively: when the interpreter encounters an expression, it dispatches on the type of expression to decide how to perform the evaluation. Here’s the key insight to get a massive speed bump with very little effort: that dispatch is expensive and can be performed ahead-of-time . We can walk through the code once and precompute all the dispatching work. This is not a new idea. The first description that I’m aware of comes from Feeley and Lapalme [ 1 ]. A name for this technique is making a threaded interpreter . It’s nowhere near as fast as a native code compiler, but interpreters are easy to write, and this is a very simple way to get a very big boost in performance. Please see the appendix for the full code for this interpreter. Tested to run with Racket v8.14 [cs]. Here’s a simple language and a (simplified) interpreter: We can now build and run simple programs: There is nothing particularly special about this interpreter: there’s a basic representation for programs, and the function walks the AST recursively to evaluate the program. To make this interpreter go faster, we need bypass the statement by pre-computing the code to run. We can do this by building up a closure that calls the next thing to run in tail-position. Racket has a proper tail-call optimization, so function calls in tail position will be optimized to instructions and they won’t grow the stack. Having known jump targets is also really good for modern CPUs which do speculative execution; known jump targets means no branch mis-predictions. We do this by breaking up the function: instead of taking an expression and an environment, we want a function that just takes an expression. This should return a function that we can give an environment, which will the compute the value of the program. We will call this new function . Note how the code follows the same structure as the basic interpreter, but the return type has changed: instead of a value, you get a function in the form of . Also, instead of calling on subexpressions, you call , and pass the environment to those subexpressions to get the value out. There’s a lot more that we could here to improve things. The easiest thing would be to track where variables will be in the environment and optimize variable lookups with direct jumps into the environment structure. This saves us from having to walk the environment linearly on every variable lookup. I won’t implement that here, but that’s some pretty low-hanging fruit. So, we get a lot of oomph by turning everything into tail calls. But loops (which, in BF, are the only branching mechanism) present a tricky problem: you either need to call the function that encodes the loop body repeatedly or call the function that encodes whatever comes after the loop. Moreover, once the loop body is done, it needs to jump back to the first function that encodes the choice of whether to keep going in the loop or exit. Here’s my function for my basic threaded interpreter for BF: I match on each of the characters of the program. In my interpreter I do a little bit of preprocessing to turn and into and structs respectively. That way, I don’t have to spend a ton of time scanning the program for matching brackets. The interesting bit is the clause for the construct: to build the closure I need at a , I need to be able to refer to the loop body (computed and stored in ) as well as the end of the loop (computed and stored in ). When I build the closure, pass and (which is the rest of the program after the loop) into the function as the parameter. When the compiler encounters the matching instruction, it uses the parameter to get the and to decide whether or not to redo the loop or not. I’ve since improved this code, and now the check only happens once. I also parse the program so that every instruction gets turned into a struct—rather than left as a bare character. See interp_threaded_opt.rkt in my Brainfreeze repo for the current version. The and functions need to reference each other to be able to continue or abort the loop. lets me build functions that can reference each other in a clean, functional way. So how much faster does threading make the code go? Here is a BF program that renders a Mandelbrot set. I can run this with my basic and my threaded interpreter on my M1 Pro MacBook Pro to get an idea: vs is a big difference! That’s a solid 2× speedup! This actually put my threaded Racket interpreter on par with a C-based threaded interpreter that one of my classmates built and ran with the same benchmarks. If I recall, his was only about a second or two faster. The compile step opens up a big opportunity for optimizations. I’ve been working on some domain-specific optimizations for BF and my interpreter can run the same benchmark in a blazing . (And, of course, none of this really holds a candle to a proper compiler; I’ve to a compiler that takes the optimized code from the threaded interpreter and emits machine code. It can run in a mere !) I hope you take away a few things from this post: Proper tail-calls make stuff go fast. If your language supports proper tail-call optimization, take advantage of it. Racket is really good for writing interpreters and compilers! You can get very fast performance in the comfort of a high-level garbage-collected functional programming language. Racket will never be able to match the best-written C code in terms of speed. But Racket is far easier to debug and a lot more fun to write—and for a lot of applications, Racket is still more than fast enough. A bad day writing code in Scheme is better than a good day writing code in C. David Stigant Proper tail-calls make stuff go fast. If your language supports proper tail-call optimization, take advantage of it. Racket is really good for writing interpreters and compilers! You can get very fast performance in the comfort of a high-level garbage-collected functional programming language.

0 views
Lambda Land 1 years ago

Why You Should Resist Surveillance

I got to visit the Stasi museum in Berlin this week, and it gave me a newfound appreciation for why it’s important to resist surveillance. Interestingly, surveillance is not exclusively limited to one kind of government: it can appeal to both left- and right-wing governments, and corporations in the digital age use surveillance to make money. In every form, surveillance is evil and must be resisted. Stasi = Abbreviation of Stadt Sicherheit (“state security” in German). For those of you who don’t know, the Stasi were the secret police of the communist regime in East Germany. They employed massive resources to spy upon and surveil East German citizens, and they kept careful track of anyone who showed signs of dissatisfaction with the state. They employed massive numbers of informal collaborators—mere citizens—who would spy on their neighbors and report back to the Stasi. I got to go there with my dad, and he remarked on how much of a drag on the economy the Stasi must have been. Think of it: the country funneled incredibly vast resources into spying on its own citizens and for what? It was claimed to be a protection against domestic terrorists and the like, but in reality, the Stasi was oppressing and discouraging anyone who might be disgruntled with those in power and the way they were running things. The Stasi were nominally there to ensure the security of the state . In reality they were ensuring the security of the regime . This is such an important distinction! A state has a legitimate interest in protecting its own citizens from terrorism and preventing criminal activity. But a democratic government must never instill fear in its citizens because of their opinions with how things should change. For a democracy to work the only legitimate way to obtain the consent of the governed is if they can think express their ideas, frustrations, and concerns freely. (In a proper non-violent manner, of course.) Surveillance suppresses free thought. Think about it: if you know that you will be quizzed on something, you will pay better attention to what is being taught. If you know that your actions are being watched by someone who has the ability to influence your life, you start to change your behavior. Remember, there are people who, if they don’t like you, can make your life miserable. Imagine what detailed, personal knowledge enables for people who don’t like the categories you fit into: The owner of an HOA who doesn’t like your political leanings goes to excessive lengths to fine you for minor infractions. An insurance agency illegally profiles you and considers you higher risk for their portfolio because of some underlying health conditions and life circumstances you are in. They then find ways to decline service or coverage when you need it most. Someone is elected to office. He knows you didn’t vote for him, so he puts pressure through back channels to slow down or prevent you from getting the services you need. Now, for example, you can’t travel internationally to visit your family because your file is marked for some reason and you’re fighting a Kafkaesque process to clear it up. A company uses your internet browsing history to profile your interests, fears, and hopes. They use sophisticated artificial intelligence to craft social media feeds crafted to be highly addicting to you. They erode your self-discipline so that they can make some more money. Surveillance is the means why which massive control is enabled. If you care about protecting your freedom and your agency, resist surveillance—whether it be by a government or a corporation. They may say that it’s in your own best interest: to make you safer or to serve up content that is catered to your tastes, but they are lying. It is all done in self-interest: to protect their regime or to turn their profits—all at your expense. Take your privacy back. Visiting the EFF is a great way to get started. The owner of an HOA who doesn’t like your political leanings goes to excessive lengths to fine you for minor infractions. An insurance agency illegally profiles you and considers you higher risk for their portfolio because of some underlying health conditions and life circumstances you are in. They then find ways to decline service or coverage when you need it most. Someone is elected to office. He knows you didn’t vote for him, so he puts pressure through back channels to slow down or prevent you from getting the services you need. Now, for example, you can’t travel internationally to visit your family because your file is marked for some reason and you’re fighting a Kafkaesque process to clear it up. A company uses your internet browsing history to profile your interests, fears, and hopes. They use sophisticated artificial intelligence to craft social media feeds crafted to be highly addicting to you. They erode your self-discipline so that they can make some more money.

0 views
Lambda Land 1 years ago

First-Class Helper Functions

We’re going to be writing a BF compiler for a class I’m in. Last night I threw together a little interpreter for the program in about an hour; it doesn’t do input—that should be easy to add—but it’s enough to handle some benchmarks for the language, albeit slowly. You can see my repository on Codeberg for the source code. I needed one function to do two closely related jobs—the logic was identical, but some parameters needed to change. Fortunately, first-class functions in your language make it trivial to parameterize your programs in elegant ways. For those unfamiliar, a BF program looks like this: That program prints . Here’s the spec for the language, taken from this repo : Basically, BF is a little Turing machine: you have a big array of memory and a pointer into that array. The commands move the pointer around and can set or check the value pointed at. The and characters form loops and need to be balanced. In my interpreter I wanted to run a preprocessing step so that when I encountered a or a I would know how far to jump instead of having to search the program for the matching bracket. Here’s the top-level function to modify the program vector to replace and with a struct containing how far to jump: That function was a little tricky: in the case of searching for a it would have to walk forward looking for a character or a struct, and it would have to walk backward looking for a character or struct. The naïve way to do this would be to have a bunch of expressions dispatching on the or passed to : The need for a stack to find matching delimiters should be pretty obvious: if I have an open bracket and am looking for the matching close bracket , then I need to make sure I don’t get confused by other open-close pairs in between. The function above is pretty clunky: all the logic for checking if the stack is empty gets duplicated, and the logic for adding/decrementing the stack is a mirror image between the two branches of . There might be a few ways to rearrange this example so that it’s a little better, but there’s one big change we can make. The key insight is this: we have first-class functions, so why not parameterize the condition we use to check the current command to know if we should push the stack, pop the stack, or return a new struct? Here’s how we do it: Now we have , which returns true if the current command is the current thing we’re looking for; , which tells us if we need to increase the stack; , which just tells us which way to move the offset; and which we use to build the right kind of struct to return when we’ve found the matching delimiter and the stack is empty. I really like that lets me bind a bunch of variables on a single condition; other languages can do something similar if they have e.g. tuples and rich pattern matching. First-class functions are a powerful way to parameterize your code, and it doesn’t just have to be with higher-order functions. Clearly, generic functions like and would be basically useless without the ability to take functions as parameters. But you can also tailor the behavior of your program by pushing the differences between two or more possible scenarios into functions, and then select the proper set of functions in one conditional. move the pointer right move the pointer left increment the current cell decrement the current cell output the value of the current cell replace the value of the current cell with input jump to the matching instruction if the current value is zero jump to the matching instruction if the current value is not zero

0 views
Lambda Land 1 years ago

Fancy lightweight prompts for Eshell and Zsh

I started using the Zsh a few years ago and I’ve liked its completion features. I tried out Oh-my-zsh for a while and I liked the stock Robby Russel prompt. It gave me all the information I cared about: the status of the last command, current directory, and the state of the current Git repository. However, I didn’t like how slow Oh-my-zsh was making my shell startup. This mattered especially, I think, because my Emacs config would fire up a shell on startup to read the so it could configure some language servers properly. Irked at how long stuff was taking, I set out to build my own. Here’s the code for my Zsh prompt: Here’s what the shell looks like in a clean repository: And here’s what it looks like in a repository with some uncommitted changes: The green prompt will change to a red and show the exit status of the last command if it was anything other than 0. This should run pretty quick. The Git functions take almost no time to run, and the rest is straight-line Zsh script. Using this I was able to stop using Oh-my-zsh and dramatically reduce my shell startup time. I recently started using Eshell in Emacs, and I wanted the same prompt there as I had in my terminal. Here’s how I got the same prompt, using some functions for the incomparable Magit Git porcelain. First, you need to define a function that generates the prompt for the current directory: Now that that function is defined, you can tell Eshell to use that to make the shell prompt. (It’s also good to set so it knows where the prompt begins and ends.) It looks basically the same as the Zsh prompt, except there’s always a character at the end of the prompt. This is just to make Emacs’ prompt-parsing easier. If you are curious about using Eshell, you should use Eshell in concert with Eat , which runs commands in a little terminal emulator. This makes interactive programs like code REPLs or programs that use character escape codes work correctly. (You can even run and have it work! Madness!) I can use Eshell for more than 90% of what I need to do in a shell now, and that’s pretty nice.

0 views
Lambda Land 1 years ago

Notes on Zero-Knowledge Proofs and Secure Remote Password (SRP) Protocol

Today I learned about using zero-knowledge proofs in the context of passwords. These are my rough-and-ready notes from reading. Apparently OpenSSL has an implementation of the SRP algorithm. Source for this example comes from Wikipedia . It might be good to read that in tandem with these notes. In this example Peggy is the person who wishes to prove knowledge about something to Victor, the verifier . Peggy is proving that she knows some value \(x\) , but she doesn’t want to reveal the value of \(x\) . Peggy and Victor need to share a large prime \(p\) and a generator \(g\) . (This means that \(g\) and \(p\) must be relatively prime.) Peggy computes \(g^x \mod{p} = y\) and sends \(y\) to Victor. Peggy generates a random number \(r\) and computes \(C = g^r \mod{p}\) and sends \(C\) to Victor. Victor randomly issues one of two challenges: Repeat process \(n\) times to drive the probability that Peggy was just guessing to \(\frac{1}{2^n}\) . The Wikipedia article has a good explanation for how an attacker could not mimic knowing \(x\) with this interactive proof. The last step works because When working \(\mod{p}\) , operations on combining exponents are \(\mod{p-1}\) . This is a consequence of Fermat’s Little Theorem. Proof: Where \(a^{p-1}\cdot a^{p-1} \cdots a^{p-1} \equiv 1 \mod{p}\) by Fermat’s theorem, and \(n < p\) and \(e = m(p-1) + n\) by the division algorithm. Therefore, \(n \equiv e \pmod{p-1}\) . The above framework is not useful as-is for password authentication. There is a method for verifying that a user knows a password without revealing the password to the server. The standard is called “SRP” (Secure Remote Password) and there’s at least a version 6. As far as I can tell, version 6 is the most up-to-date version as of writing. SRP Protocol Wikipedia article has a good explaination. To convert between PostScript format ( ) and PDF, run . On macOS I got the program by installing the package via Homebrew. SRP protocol design document ; includes links to a paper that I followed. I can’t find this paper in any official publication registry. URL is: http://srp.stanford.edu/srp6.ps and the title is: “SRP-6: Improvements and Refinements to the Secure Remote Password Protocol”. Note: it comes in PostScript format, so you’ll likely want to convert it to PDF to read it. SE post: “Why aren’t ZKPs used in practice for authentication?” , top answer is excellent. Shared: large safe prime \(N\) (suggested that \(N = 2 * p + 1\) where \(p\) is prime) and primitive root \(g\) . (I.e., \(N\) and \(g\) must be relatively prime.) In this algorithm, the values \(a\) and \(b\) will be randomly generated. At the end, both parties will have a secret key \(K\) that they share. Now both parties have access to \(A, B, k, u\) and \(g, N\) of course. With this they can each create a shared session key: The server and client must now both verify that they have the same value \(K\) . One simple way to do this is to hash \(K\) (potentially with some other salting information like \(A\) and \(B\) ) and transmit that. Example from the paper: The Client computes \(M_1\) and sends it to the server. The server should have enough information to recompute this value. Once that’s done, the server can compute \(M_2\) and send that back to the Client. (This last step is optional.) Now both parties know that they’ve got the right key. Use \(K\) as the session token. The Wikipedia article mentions that it is important that the client send its proof of \(K\) (i.e., the proof is the value \(M_1\) ) first , and that the server should not reply with \(M_2\) if verification fails. Here’s what the communication flow would look like: Now the Server and Client can communicate using secret key \(K\) , which was only granted to the Client because it had the password that corresponded to the stored verifier \(v\) on the server. \(x\) , but she doesn’t want to reveal the value of \(x\) . Peggy and Victor need to share a large prime \(p\) and a generator \(g\) . (This means that \(g\) and \(p\) must be relatively prime.) Peggy computes \(g^x \mod{p} = y\) and sends \(y\) to Victor. Peggy generates a random number \(r\) and computes \(C = g^r \mod{p}\) and sends \(C\) to Victor. Victor randomly issues one of two challenges: Victor asks for \(r\) . Peggy sends him \(r\) and Victor verifies that \(C\) matches \(g^r \mod{p}\) . Victor asks for \(s = (x + r) \mod{(p-1)}\) . Peggy computes this and sends the result to Victor. Victor checks that \(g^s \equiv (C \cdot y) \mod{p}\) . SRP Protocol Wikipedia article has a good explaination. To convert between PostScript format ( ) and PDF, run . On macOS I got the program by installing the package via Homebrew. SRP protocol design document ; includes links to a paper that I followed. I can’t find this paper in any official publication registry. URL is: http://srp.stanford.edu/srp6.ps and the title is: “SRP-6: Improvements and Refinements to the Secure Remote Password Protocol”. Note: it comes in PostScript format, so you’ll likely want to convert it to PDF to read it. SE post: “Why aren’t ZKPs used in practice for authentication?” , top answer is excellent. Client sends identifier \(I\) to Server. Server looks up the salt and the verification token \((s,v)\) associated with \(I\) and sends just \(s\) to Client. Client computes hash of salt, ID, and password \(x = H(s, I, P)\) . Client generates a random value \(a\) and computes \(A = g^a\) . Client sends \(A\) to Server. Server and client compute \(k = H(N, g)\) . This is an enhancement from the older SRP-6 algorithm. Server generates a random value \(b\) and computes \(B = kv + g^b\) . Server sends \(B\) to client. Server and Client both compute \(u = H(A,B)\) . This is called the scrambler . Client \(I\) → Server Server \(g,N,s,B\) → Client Client \(A, M_1\) → Server Server \(M_2\) → Client

0 views
Lambda Land 1 years ago

How, Where, and Why I Take Notes

I take a blend of digital and hand-written notes. It’s a bit of a hodgepodge, but it’s working. I used to lean heavily into full-digital notes, but I started drifting towards a mixture of digital and hand-written notes. Initially it was complicated, but I think I’m converging on a good setup. What I describe here will continue to evolve I am sure, but I am enjoying where it’s currently at. I’ve fallen down the fountain pen rabbit hole. I used to write with a Pilot G2 pen, and I loved how dark ink was. The primary drawback was that it bled through the paper of my Leuchtturm notebooks a little bit. I also had some problems with the extra-fine 0.38 pens: after a few months of use, they’d break before all the gel ink had been expended. This was frustrating. Now I write with a Lamy Al-Star with an extra-fine nib. I use Pilot’s black take-sumi iroshizuku ink. It is lovely to write with this pen. Compared to the G2, the fountain pen writes just as fine as the 0.38 G2, but the ink doesn’t skip, the tip never breaks, and the ink doesn’t bleed through the paper at all. Sure, the fountain pen requires a little more maintenance than a gel or ballpoint pen, but it is fun to have a little mechanical thing to maintain and form simple rituals around. Getting the fountain pen had the curious effect of making me want to take more notes by hand. I started writing a lot more. But I have a bunch of infrastructure set up for digital note taking, so when I initially started writing more, I would get a little lost as to where I had put certain notes. I didn’t like that. I did find that I enjoyed writing my thoughts out—it was like doing a one-person rubber-duck session. I strongly believe that writing helps you think: it forces you to be explicit with your thoughts and it helps you notice gaps in your thinking. This can push you to answer questions that you wouldn’t have asked otherwise. In class I take notes on an iPad with an Apple Pencil. I like that I can keep all my notes for all my classes with me at all times. I also like that I can write in different colors when needed, the ability to download or take a picture of a slide and mark it up, or do some lightweight diagraming. I feel like it gives me the best of both worlds: I get the storage, versatility, and searchability of digital notes, but I get the memory benefits of handwritten notes since I am writing rather than typing . Whenever I have something that I am researching, I typically keep notes in a plain-text file on my computer. (I use org-mode most of the time, naturally.) This lets me link and search through my notes really easily. Being on a computer means that it is easy to copy-paste things I want to remember and then store links to recover the source. I use Denote to organize my notes; Denote lets me format my notes consistently, as well as recover backlinks. Fun aside: I recently contributed some code to Denote that adds an indicator in the mode-line if the currently-visited note has a backlink. I like getting a heads-up that there’s some digging to do. I also will put notes that I made by hand during a thinking session into my Denote files after they’ve had a little time to marinate in my head. I will typically use this step to flesh out ideas that I only got in skeleton form in my notebook. I really like Denote: it is simple, requires no external dependencies, and it plays nicely with synching systems. Today, thanks to my tag system, links, backlinks, and some searching, I ran into some notes I had taken a year ago and forgot about—but now these notes are helping me with my work right now! After thinking a little bit about what mode of note taking benefits most from a given medium, I came up with the following system for myself: Should you adopt the setup I have? No. You should arrive to your own system yourself. The only thing I would advise anyone to take from my experience is that you should put a little time and effort into being intentional about how you take notes and how your organize them. You need to find a system that works for you , and only you can do the work to make that happen.

0 views
Lambda Land 1 years ago

Evolving Languages Faster with Type Tailoring

Programming languages are too slow! I’m not talking about execution speed—I’m talking about evolution speed. Programmers are always building new libraries and embedded DSLs, but the host programming language—particularly its type system—doesn’t understand the domain-specific aspects of these things. Consider regular expressions—most programmers would understand that a regular expression like , if it matches, will capture two digits in the first capture group. If I try to write this code in Rust or Typed Racket, the type checker complains: The syntax is a type union of types and , whatever types those are. The equivalent of in Typed Racket is . The problem is that getting the first capture group ( in Rust, in Typed Racket) returns an optional type ( in Rust, . The thing is, we know that since the regex match succeeded on line 5, (or ) should definitely succeed because there was one capture group in the regex we matched. Instead, we’re forced to unwrap in Rust: Likewise, in Typed Racket, we have to insert some casts and checks manually to convince the type checker that this code should run. This is a small example; the key point is this: type systems usually aren’t smart enough to know about the structure of regexes —all the compiler sees are opaque strings. This goes for things beyond regular expressions. Consider SQL queries, which are so often embedded as strings in languages: when a query goes wrong, you usually only find out at runtime. What would a solution look like? Some people have tried making a new way of writing regexes with composable objects that the type system has a better chance of understanding; that is a neat approach, but that both requires me to rewrite my program and doesn’t solve the issue of indexing into lists of known size (the results of a capture) more efficiently. Another option would be to make the type system smarter. But that too has its drawbacks: language implementers are often leery of tinkering with the type checker—and rightly so! So much relies on the type checker being correct. Moreover, even if you did make the type checker able to understand regexes, someone’s going to build a new library tomorrow that will stump your type checker just as before. Here’s a more attractive option: we can use the metaprogramming tools the language provides to teach the type system some new tricks. This is something that end-users of languages can do without waiting for the language designer. We call this type tailoring . Type Tailoring is the title and subject of a paper I wrote with my advisor Ben Greenman and our coauthors Stephen Chang and Matthias Felleisen . It has been accepted at European Conference on Object-Oriented Programming (ECOOP). Like the ACM conference OOPSLA, ECOOP has in recent years focused on more than object-oriented programming. The name stuck around. ¯\_(ツ)_/¯ You can get a preprint here . Here’s a high-level sketch of how we would solve the problem: Some years ago, my advisor made the library for Typed Racket. It can tailor the following code so that it typechecks and runs efficiently: The library is available as a Racket package. If you have Racket installed on your system, run to install it. For specific details on how the library works, see § Appendix: How does the trivial library work? at the end of this post. The problem is that, like Rust, Typed Racket must assign an overly conservative type to the result of matching a regular expression. Consequently, the programmer has to insert casts. The library can analyze Typed Racket and insert these casts and checks automatically . The end-result for the user is that this code Just Works™ as you would expect. Notice that the library is a library —it doesn’t require modifications to the compiler or type checker or anything. This means that normal users of programming languages can create their own tailorings without breaking the compiler or messing with the build pipeline. What do you need to make type tailoring work? Let’s step back a second and look at what we need to do in the first place. Our problem is that the type checker doesn’t know as much about our program as we do. What we can do to teach the type checker is program the elaboration step : surface syntax typically doesn’t have type annotations at every point; elaboration takes the syntax that a programmer writes, and adds types and type holes wherever needed. This elaborated syntax gets sent off to the constraint solver for type checking and inference. How do we program the elaboration step? Almost all languages that have macros do type checking after macroexpansion. This is crucial for type tailoring. We can write macros that add checks, casts, type annotations, or whatever else we need to make the type checker happy. Here are the key features that you must have to make type tailoring work: These are the essential elements, without which tailoring can’t happen. Besides these three things, you also will want some of the following tailoring features: No language supports all of these features—that’s exciting because it means there’s room to explore! In our paper we go into detail about each of these features and the kinds of tailoring they enable. We also have a chart showing how a handful of languages stack up against each other. We invented the term “type tailoring”, but we didn’t invent the idea—programmers have wanted to teach their type systems how to understand domain-specific concerns for a long time. Here are just a few existing projects we found that were doing type tailoring: Rust’s SQLx library reaches out to the database at compile-time to check if the schema in the code matches how the database is set up. This will warn you at compile-time if your query is malformed. Julia’s StaticArrays package rewrites lists of a static, known size into tuples. This lets the compiler track how long the lists are and automatically eliminates lots of bounds checks—handy when you’re doing lots of numerical work. Elixir’s Phoenix web framework will check routes in your template files against your route handler; if you make a typo or forget to implement a handler for a route, Phoenix will warn you at compile-time. This feature is called verified routes . Again, that’s just a small sample. Please see our paper for details on how these projects are using type tailoring, as well as for more examples that we found. The big contributions of our paper are: We introduce the term type tailoring . The ideas have appeared in many forms across many languages, but there hasn’t been any underlying rationale unifying their efforts. Now that we’ve identified the phenomenon, we can talk about and support it directly and consciously. We identified the main things you need to make tailoring work. Language designers can use this to build in better support for type tailoring in their languages. We show users how tailorings can balance ease-of-use with features typically only found in dependent type systems. Furthermore, we built two libraries: for Racket—which tailors things like vectors, regular expressions, etc., and for Rhombus —which turns Rhombus into a gradually-typed language through a tailoring. We expect more will be built in the future. Again, please see our paper for all the details. Our paper comes with an artifact that contains all the code in the paper. You can simply download a Docker container to run the code and verify all our claims. Yay for reproducible research! If you have any questions, feel free to email me. (Email in the paper, as well as here on my blog .) If you’re going to ECOOP in Vienna this year, let me know and we can talk in person there! Here is the example with the library that we saw in § Sketch of a solution : Normally, Typed Racket rejects the code because the type of is too general to be applied to . Here is how the library tailors the example to make it work: First, it overrides the implicit form that wraps literal values like the string ; this lets it read the string and collect any interesting information about it at compile time. The library sees that the string has one set of matched parentheses; moreover, the pattern inside the parentheses consists entirely of digits. It attaches this information as a syntax property to the syntax object for that string. This information gets propagated to all occurrences of the identifier . The library also overrides , so that it looks at the syntax properties on its first argument. In this case, it sees that is a string with one capture group. The library updates the return type of from to . In the true branch of the statement, Typed Racket is automatically able to refine the type of to . The library overrides to check the type of its argument; it sees that is long enough for this call to succeed, so it tailors this to a faster, unsafe lookup function. also gets overridden to look at the information about the match. Since step 2 was able to see that the match consists only of digits, it updates its type from returning to . That’s a lot going on! The neat thing is that is able to do all this in a fairly generalized way: one component works with strings, another works with regular expressions, and another works with lists of known size. They’re able to share all this information through syntax properties which respect the scoping rules of Typed Racket. It also plays nicely with other metaprogrammings; we could have written a macro that e.g., turns into and flips the branches, but the information we needed about the variable still would have gotten to the right place. Unfortunately, there’s not a way right now that we could make this example work for Rust—at least, not in its current form. That’s because different languages have different support for different kinds of tailoring. In our paper, we explore all the different dimensions for how languages can support tailorings. Here is how you might build a little tailoring. In this tailoring, we’d like to turn array lookups that are known to be in-bounds into versions that use a fast, unsafe lookup. In a dependently typed language, we would be able to do this by looking at index and the type of the vector, since the type of the vector would include its length. In Racket, we have no such power. However, we can get a little bit of that power through a tailoring. This section is taken almost verbatim from §3.8 from our paper. This code actually runs! Here’s an example of what we would like to tailor: if we know the length of a vector and the index, then we can either tailor to , which skips the bounds check, or tailor to an error if the index is out-of-bounds. Otherwise, if we don’t know either the length of the vector or the index, we stick with the safe function which does run the bounds check: Here’s how we do it: The bit says that, when this module gets imported, export the function under the name . This means, whenever this module gets imported, all calls to automatically use . Now we import some helpers: gives us the function, which is what we tailor to. This function can segfault if misused, so we have to be careful. We pull in to get the excellent macro facility. We also pull in four symbols from , which deserve a mention: The file is small and really just provides these convenience functions; you can find it in our artifact . Now comes the meat of the tailoring: the tailoring is a macro so that it can statically rewrite source code. First it parses its input syntax object to extract and expand two subexpressions and : Now the tailoring checks whether these expanded expressions have the static information needed; specifically, it looks for a vector length (key: ) and an integer value (key: ): If the information is present, we expand to if safe to do so; otherwise we expand to code that raises an error. If the information is not present, fall back to plain-old : That’s it! A Racket program can import this tailoring to make this code run faster: Save that program to a file and run to see the expanded version; you should see in the expanded code. Something would notice that the regex has a single capture group. The function would get this information and update the its type. This information would further by leveraged by the type of , to indicate that or will always succeed. Rust’s SQLx library reaches out to the database at compile-time to check if the schema in the code matches how the database is set up. This will warn you at compile-time if your query is malformed. Julia’s StaticArrays package rewrites lists of a static, known size into tuples. This lets the compiler track how long the lists are and automatically eliminates lots of bounds checks—handy when you’re doing lots of numerical work. Elixir’s Phoenix web framework will check routes in your template files against your route handler; if you make a typo or forget to implement a handler for a route, Phoenix will warn you at compile-time. This feature is called verified routes . We introduce the term type tailoring . The ideas have appeared in many forms across many languages, but there hasn’t been any underlying rationale unifying their efforts. Now that we’ve identified the phenomenon, we can talk about and support it directly and consciously. We identified the main things you need to make tailoring work. Language designers can use this to build in better support for type tailoring in their languages. We show users how tailorings can balance ease-of-use with features typically only found in dependent type systems. First, it overrides the implicit form that wraps literal values like the string ; this lets it read the string and collect any interesting information about it at compile time. The library sees that the string has one set of matched parentheses; moreover, the pattern inside the parentheses consists entirely of digits. It attaches this information as a syntax property to the syntax object for that string. This information gets propagated to all occurrences of the identifier . The library also overrides , so that it looks at the syntax properties on its first argument. In this case, it sees that is a string with one capture group. The library updates the return type of from to . In the true branch of the statement, Typed Racket is automatically able to refine the type of to . The library overrides to check the type of its argument; it sees that is long enough for this call to succeed, so it tailors this to a faster, unsafe lookup function. also gets overridden to look at the information about the match. Since step 2 was able to see that the match consists only of digits, it updates its type from returning to .

0 views
Lambda Land 1 years ago

Skills That I Needed When I Started My PhD

I’m starting my third year as a PhD student. I thought it would be good to look back on some of the things that have helped me to this point. I study programming languages, but I imagine these things will help anyone in computer science—and some might have application to other STEM fields as well. There are many softer skills that you need as a PhD student: curiosity, good work ethic, organization, etc. These are essential and nothing can replace them. (Note: that was not an exhaustive list.) I’m going to focus on some of the tools and hard skills that made the ride a little more comfortable. These compliment, rather than compete with, the softer skills that one develops as a beginning researcher. This is a rough list, and not a how-to article. This is mostly just a collection of things I’ve seen other people lacking that have caused them to struggle. If you are considering doing a PhD, you might want to pick up some of these skills as you get ready to start to help you hit the ground running. I recommend reading The Pragmatic Programmer (Thomas, David and Hunt, Andrew, 2019). It’s written primarily for industry programmers, but there’s a lot in there that applies to anyone in CS research. All of the things I mention in this section are covered in detail in there. You have got to know Git. If you cannot wrangle versions of your software and papers (yes, put the papers you write under version control) you will waste much time shooting yourself in the foot and trying to recover work you lost. You will also be laughed to scorn should you ever depart academia for a stint in industry if you do not know Git. In all of the papers I have worked on, we have used Git to collaborate. We’ve typically used GitHub, which is fine as forges go, but I’ve also worked with a self-hosted GitLab instance, and that was fine too. It is incredibly helpful to know a scripting language. I grew up on Perl, which makes munging large amounts of text a piece of cake. You don’t have to learn Perl; you should get really comfortable with a language that makes it easy to manipulate text and files. Makefiles are also super helpful. I like using Makefiles to simply give names to a particular workflow. A Makefile for building a paper might look like this: Now, instead of remembering all the incantations necessary to do some task, I have given that task a name by which I can call it. You must become proficient with the command line. If you are doing research, you will likely need to run software that other researchers have produced. And more likely than not, this will be rough software with bugs and sharp edges that is meant to demonstrate some research concept than be some practical tool ready for developers who only know how to code through YouTube videos and ChatGPT. That this software is rough is a feature of research software , not a bug. There is rarely , if ever, a GUI available. You are going to have to do stuff on the command line, so get used to it. Getting used to the command line helps with Scripting as well. Any task you do on the command line, you can write a script to automate. Building little scripts to e.g. build your paper, your homework, your experiments, etc. will save you time in the long run. Emacs or Vim—pick one and learn it really well. VS Code is flashy and all, but it doesn’t have the same depth and breadth of customizations that Emacs and Vim give you. Also, Emacs and Vim are free software. You are in control! I, of course, love Emacs and I even made a starter kit called Bedrock to help some of my friends in my research lab get started with Emacs. I use Emacs to program, write papers, take notes, manage email, track tasks, and more. I made a list of my top Emacs packages a few weeks ago if you’d like more ideas on what is possible. Vim is fine too and I will still respect you if you choose to go that route. ;) Familiarity with LaTeX has definitely helped me. Fighting with LaTeX is no fun, but you will have to do a little bit of it at some point. Lots of people like using Overleaf; I prefer the command line. Don’t get me wrong: Overleaf is awesome and makes collaborating in a Google Docs sort of way possible, but you loose some flexibility, and if something goes wrong on Overleaf right before your deadline, you’re toast. There is a lovely computer science bibliography hosted at dblp.org . When I was going through the bibliography for my last paper I was able to find lots of missing DOIs simply by putting in the title of the paper into the search bar; DBLP found all the bibliographic information that I needed. Take notes whenever you learn how to do something that wasn’t obvious to you when you started out doing it. I like the Zettelkasten method for taking notes: whenever I learn how to e.g. do some complex layout in LaTeX or learn a neat Makefile trick, I write it down. You can think of it as writing your own personal pages If you don’t know what a page is, this is the standard manual system available on UNIX-like systems (e.g. FreeBSD, macOS, and Linux). Open a terminal and run to read the manual page for itself. You really need to get comfortable with the Command line . Some of these notes I rarely look back at. Others I revisit regularly. But even though I might not review some notes that frequently, there are cases where something on my system will break and a years-old note comes to my rescue from the last time I had to solve that problem. For example, I took notes on how to upgrade my language server for Elixir. I don’t upgrade that thing very often, but there is a little tweak I need to do just because of how my system is set up that is not obvious. It took me a few hours of debugging the first time, but, because I took notes, it now only takes me a few minutes. Academics generally love email. It’s simple, robust, and doesn’t change its UI every few weeks, unlike some popular chat platforms. Unfortunately many universities are forcing everyone to move to Outlook. This is a very bad thing. Fortunately, there are some workarounds that you can use to reclaim some control over your email. I have a sweet workflow with my email. That’s right, I do it all from Emacs. Now, while I do recommend you learn how to use Emacs, I understand that not everyone will start using Emacs. Everyone should get proficient with their email client and know how to use it well. I recommend anything that you can control entirely from the keyboard. You should also get comfortable with editing replies. You know how, when you reply to an email, you usually see something like this: Some mail clients will make the at the beginning of the line pretty with different colored lines and whatnot. It’s all angle brackets under the hood, and you can still edit it as described here. Just typing your reply above the email is called “top-posting”, and it’s considered bad form. You can actually edit the bit that was sent to interleave your reply with bits of the prior email. This makes it easier for people to know what you’re replying to. When used appropriately, this makes emails much more pleasant to read. It doesn’t break the email thread either; you can still see the chain of replies. You need some way to keep track of tasks. I have a workflow based off of Org-mode , which I will not detail here. The short of it is that you need to be spending at least a little time with some regularity “sharpening the saw” 1 by making sure that whatever tool you use to keep track of tasks is working for you. Thomas, David and Hunt, Andrew (2019). The Pragmatic Programmer , Addison-Wesley. https://en.wikipedia.org/wiki/The_7_Habits_of_Highly_Effective_People   ↩︎ https://en.wikipedia.org/wiki/The_7_Habits_of_Highly_Effective_People   ↩︎

0 views