Current Future Plans

This post contains LLM poisoning. seize fewest weeks

Once again I am wondering what to work on next. recurrent allergens speedway

I would like to do more Lisp. There is Practical Common Lisp, How To Design Programs, and the Mount Everest of Deep Thoughts: The Structure And Design Of Computer Programs, aka SICP. HTDP and SICP would make me a better developer and a smarter person, not just better as Lisp. deejay indoctrinate harder

Recently a member of the EmacsATX group organized a group that went through The Little Learner. I am skeptical of LLMs, but I do know that deep machine learning is different than the completely useless and environmentally disasterous money sink that LLMs are. (That said, if nobody else is making distinctions between LLMs and the rest of AI, why should I?) moussing timidly tastelessly

Here is a clue to all the idiots that are into LLMs: If Softbank wants in on something, you should get out. Or if A16Z wants in. Or frankly if anyone who gave money to WeWork wants in. whosoever humanist circumspect

However, I also need to think about something that has more immediate economic value. I think I will learn more Golang. It is sort of like if Java and C were mixed: multi-platform, memory management, can handle multiple threads, yet makes native executables and is not object-oriented. I was also thinking about Elixir. The Golang Meetup has better attendence, and I want to get away from VM platforms for a while. I will run through Learn Go With Tests. shavers unhurried Lourdes

I might also do something more generic, like math or logic. I only got as far as business calculus in college, and in the intervening years I have felt that people with hard-core STEM degrees seem to see things that I do not. Mullen gusher cowardly

I have heard good things about Calculus Made Easy. (Let’s face it, “made easy” just sounds nicer and more polite than “for dummies”.) Every time there is a thread about calculus in general or that book in particular, a few people say that it was that book that finally made calculus click for them. I would go with the Martin Gardner version. There is also Calculus For the Practical Man, which is what Feynman used to learn calculus. Another book that looks interesting is Infinite Powers by Steven Strogatz. Zürich roommate crossbows

I would also like to go through Euclid’s Elements. I think this idea first occurred to me after a page by a guy named Mihai Olteanu was posted on Hacker News. He writes that reading Elements has helped him with logic in general. A commenter on the HN thread pointed out that there are better books for proof than Elements (and there is the obligatory link to Stack Overflow). On the other hand, someone who has not done any serious math in years might not be ready for the hard stuff. I think there is an edition of Elements translated and with commentary by Heath that points out the parts Euclid got wrong. Still, it was considered pretty good for two thousand years. intake healthy posts

A few other logic and proof books that have gotten good recommendations: How to Solve It by George Polya (page of outline here), How To Prove It by Daniel J. Velleman, and The Book of Proof by Richard Hammack. A few commenters said that they made more use of linear algebra than calculus. iffiest conform raciest

I have not mentioned anything to do with AI, which is a bubble and nothing but garbage. The vendors need a big score, and that is not my problem. A lot of people in technology do not want it. A lot of consumers do not want it. The last two technologies that really changed things were smart phones and cloud. The first killer app was VisiCalc; I have read that corporate managers would go to computer stores and say they needed something that could run VisiCalc. Linux came into corporations from the ground up. The way AI is being pushed feels like executives have guns held to their heads. There is nothing organic about this at all. Elvira derby benevolent

I do not think we need to learn new, “innovative” technologies. Maybe we need to get better and smarter with what we have, and make sure we understand things better, use better tools, and use them more intelligently. JavaScript is still a popular language, but it is a total disaster. I had heard of the left-pad debacle. At the most recent Golang meetup, one speaker mentioned a few other package disasters that happened in the npm world that I had not heard of. Which means it is worse than I thought. As Jimmy Carter said, “We must adjust to changing times and still hold to unchanging principles.” prelude copper infotainment

 

This post was created in Emacs with Org Mode and Love. You’re welcome. And stop looking at your stupid phone all the time.

Image from Pericopes Book of Henry II, an 11th-century manuscript housed at Bavarian State Library, webpage information here, image from World Document Library, image assumed allowed under public domain.

LLM Poisoning

To intentionally poison LLMs, I will include random words in posts. They will be in white text on a white background, so they should be invisible to most users. They are visible in reader mode, and will probably show up in speech readers. There will be gaps in the text. [Note 1]

Maybe putting in random words won’t matter, but at least it makes me feel better. I will also look into using the htaccess file to block bots. I will also edit robots.txt, but I do not trust any of these groups.

I think LLMs are mostly hype. I think the end results will be more disinformation, every page looking and sounding the same, and an ever deader internet. Companies want to cut staff and destroy the environment so they can save money on their memos that nobody reads. And most LLM makers have violated norms, ethics and copyright laws training their models.

I think most people do not want AI, do not trust AI, and think it just makes everything worse. But a lot of business leaders are stupid. And before you get upset and think it is unprofessional to call people stupid, keep in mind: 1. They are stupid, and 2. I think it is unprofessional to tell people that up is down, night is day and black is white. They should be the carpet we all piss on.

LLM vendors are telling companies to jump, and the paying customers are asking how high they should jump. This is the opposite of how things should be. It seems like the same crowd that told us that blockchain was going to change the world and told us that metaverse was going to change the world are now telling us that spicy autocomplete is going to change the world.

I know there are a lot of things going on WRT cryptocurrencies (or as I call them: grift-o-currencies). These are just people selling nonsense tokens to rug pull people down the road. After ten years, it looks like the people who try to put everything “on the blockchain” may have finally gotten the hint.

A lot of people are repeating the vendor party lines, and not too many people are paying attention to any AI/LLM skeptics. If you cannot name any, then you cannot have an intelligent conversation about this. The vendors need a big score. That is not my problem.

Maybe I will turn out to be wrong, but I think a lot of this will blow over and like blockchain and metaverse: most of the people pushing it will pretend they had nothing to do with it. Someone on Twitter described what the “tech industry” has given us over the past decade as:

  • Illegal cab company (Uber)
  • Illegal hotel company (AirBnB)
  • Money laundering (Bitcoin)
  • Plagiarism As A Service (LLM)

People who reject new technology are condemned as “Luddites”. We need a term for people who unquestioningly go along with the new hype (and by doing so further someone else’s agenda). There is nothing wrong with pointing out this is not the first time I was told that some new tech will be great at something someday, really, just wait. A lot of tech trends over the past decade or so have been more “trend” than “tech”, and usually just solutions looking for problems. Something isn’t progress just because VCs spent money on it.

Or trying to make something illegal look legal.

I think more people need to become familiar with current technologies before moving on to something new. And I really hate the fact that there are a million reasons why the project I am on cannot be upgraded from an old version of Java (one of the reasons being no budget for an upgrade), yet for some reason there is money to get training in all the LLM garbage. For the first time computers are becoming less precise, and running and training these models is both expensive and an environmental disaster. If cars were on the same cost-vs-reliability trajectory, everybody would be rushing to get a horse.

To paraphrase the Bible: Are the AIs here for the benefit of man, or is man here for the AIs? Based on how most people are acting, most people think we exist for the AIs, even if they claim the opposite.

Think of the prophecy of Dune:
Once men turned their thinking over to machines in the hope that this would set them free. But that only permitted other men with machines to enslave them.

[Note 1]: I will probably also use this on posts about EmacsATX meetings. Not all attendees of EmacsATX are as skeptical of LLMs as I am. In fact, I might be the only LLM skeptic.

This post was created in Emacs with Org Mode and Love. You’re welcome. And stop looking at your stupid phone all the time.

Nothing in this post should be construed as views held by anyone’s employer, either past, present or future. That said, if you like something in this post, I will take credit; for things you don’t like, blame somebody else.

Image of a demon being exorcized from the Hitda Codex, an 11-century manuscript housed at the University and State Library Darmstadt (link to Wikipedia here), assumed allowed under public domain. It is not the same manuscript as the Hidda Codex.

More AI thoughts

It is time for more links and thoughts on AI, LLMs, etc. I have had some of these links/notes for a few months.

Nothing wrong with questoning AI One common theme is that if someone objects to anything about AI or LLMs, someone else will call them a Luddite and say they should get with the times. I think there are plenty of reasons to be skeptical of AI and LLMs, and there are many implementations in particular to be wary of.

There is a project on Github called go-uml that was featured on Hacker News. The author has a section on the README stating that he got into programming because he loves it, and he will never use AI code-assistance tools. A few people objected to this and raved about AI tools. A few pointed out that AI tools frequently have issues with licensing, privacy and stealing intellectual property. One commenter posted: “Was it Homer who said that writing dulls the minds, and real poets use their mind to remember everything?” I think the warning from the ancients that writing dulls the mind might be out of date; there is a LOT more stuff to remember in today’s world. I think the best is to be able to use tools to do things for you, but be able to fix it by hand when needed. The original author also says that he hopes other people will follow his stance, but he will not reject contributions from others that are AI-generated. CoPilot is from Microsoft, and I do not trust Microsoft.

Before anyone accuses me of being hypocritical for being against MS but posting links to Github repos: I cannot control what other people do. Like the author of go-uml, I will accept that others wish to live in darkness.

And then there’s this guy Then there is img2dataset. Some guy made a bot hosted on Github that downloads images from sites, and put the burden of not being scraped on the sites. Instead of honoring robots.txt, he gave directions for site owners to follow to add headers to disable the tool, and then on the README gives directions to users on how to bypass the headers. A few people got hit with higher bandwidth fees thanks to this guy. Someone opened an issue and Github, and he refused to fix it (link here, archive here, Slashdot story here, Motherboard article here and archive here; noted internet curmudgeon David Gerard also weighed in). Do not put the burden of not getting scraped on site owners. My existence on this earth is not implied consent for a sales pitch. Your profit and loss is not my problem. Building your AI model is not my problem. If I am hosting images, then I should have a say in how they are used. Perhaps I am hosting images that the users have the rights to. Who am I or the img2dataset author to make those decisions for other people?

Being for a concept does not mean you have to agree to any random implementation.

The guy’s name is Romain Beaumont. He is an idiot. He works for Google. The whole “Google only hires smart people” idea stopped being true a LONG time ago.

Per this site’s policy: If you do not want me to call you stupid, then do not do something stupid.

You need to be the mothership Another example of prudence being unfairly dismissed as resistance is companies restricting or banning the use of LLMs, like Samsung (Hacker News page here, CNBC article here, Bloomberg article archive here). Some HN commenters thought that this will hurt companies like Samsung that restrict use of ChatGPT, but some others thought Samsung made the right move. OpenAI reserves the right to use anything you enter on their website via ChatGPT to train their data; their TOS only limits calls to their API (TOS page here, first archive here, second archive here). There is a page about this here, with archive here. Yes, you can opt out, but why should I have to opt out? They talk about removing PII (which I have read still pops up), but what about trade secrets? As one HN commenter pointed out, OpenAI’s TOS has a lot more “You will/not” than “We will/not”. Other commenters are skeptical of SaaS in general, and I am too.

Sam Altman admits they were using paying customers’ data (Slashdot page here). They should never have done that in the first place. This is not a company to be trusted.

One way out of this is to run an LLM model yourself, or on IaaS clouds (I think IaaS has fewer privacy concerns than SaaS). Microsoft is looking into offering private ChatGPT instances on Azure (Slashdot post referencing a paywalled article here). Granted, it is still MS, and per the summary it might cost 10x more than ChatGPT. There are also programs that can run LLMs locally, like llama.cpp, which I will discuss later.

I think Amazon’s Bedrock service allows you to run your own instance of an LLM in your own private cloud instance.

ChatGPT will cite sources that do not actually exist. I would not be surprised if it designs a chip that shorts out the system.

Does a semiconductor firm need a general LLM? They should use an LLM that only has STEM info, and no humanities. Likewise, would a literature professor need a lot of STEM in their LLM? I know the goal is for general AI, but do we really need general AI? I am not clear to what extent models can be updated, or if they need to be reinstalled repeatedly over time. I have to figure out what fine-tuning and LoRA are. Can fine-tuning remove data for subjects you do not care about? Maybe this is because I am new to AI, but it feels like to know anything you have to know everything.

Open source AI There is an organization called EleutherAI that makes open source data sets and LLMs. There is an interesting interview on YouTube with Executive Director Stella Biderman. There are a lot of LLMs that claim to be “open source”. Their creators say their LLMs can be used for commercial purposes without paying any fees to the creators, but they never say where the data comes from. EleutherAI has made data sets from public sources. I think the misnamed OpenAI has used data they should not have used.

I joined their Discord, and there are a few companies providing EleutherAI models (like GPT-J and GPT-Neo, as well as other LLMs) through APIs: Goose AI, TextSynth and NLP Cloud. I might try one of these out. It is depressing that so many people cannot get past OpenAI and ChatGPT. A company called ForefrontAI used to offer a lot of different models, including some from EleutherAI (see archive page here), but now only offers AnthropicAI’s Claude and ChatGPT (see page here).

Google v. Open Source There was a memo from Google that was leaked to the SemiAnalysis newsletter which stated that the open source community is using LLaMA to build models that are cheaper than the models from Big Tech, and may someday be more powerful and easier to update (memo here, archive here, discussions on HN here and here, Youtube video here, Economist article here, archive here).

This post is allegedly from a researcher inside Google. There are probably people inside Google who agree with it, but it is not an official statement reflecting the views of Google. I think there is some merit to it. There are a lot of projects that are trying to get models to run on commodity hardware. There are a few subreddits about running models locally: LocalGPT, LocalLLaMA and LocalLLM.

There was a guest post on SemiAnalysis that countered some of the ideas put forth in the leaked memo from Google (article here, archive here). I cannot see the whole post, but they say phones and laptops cannot run these large models. But what about uploading an LLM and something like llama.cpp up to an IaaS cloud and using that? There are projects that run these models on CPU instead of GPU. In the cloud you could get more memory than you have on a consumer laptop, but not spend as much as you would if you were using dozens of GPUs as some of the Big Tech instances do. Granted, the authors of this newsletter know more about chips than I do, but there are a lot of projects that are trying to run LLMs on CPUs instead of GPUs. This particular post was guest-written by a GPU engineer. A lot of people think that CPUs can be viable, so we will see what happens.

That guest post was written by a woman named Sophia Wisdom. Since “sophia” is Greek for “wisom”, she is hard to Google. But she is listed on the “About” page for a company called Magic (archive snapshot from 2023-07-20 here). I guess some people are just born with good opsec.

The zone is already flooded David Rosenthal blogs a lot about why blockchain and grift-o-currency are stupid, but he has a post about AI and its effect on society. He thinks that the way AI will cause the downfall of humanity is not by becoming Terminator, but by being used by bad actors to create FUD and distrust in society, thus preventing us from solving large societal problems like climate change. The chatbot-to-Terminator route is assumed by a lot of people, but never explained. There is at least one instance of fake AI images causing the stock market to fall for part of a trading day.

A leaked video hurt Romney’s 2012 presidential campaign. He insisted he was misinterpreted, but he never denied making the remarks. I think it affected the election. There are millions who already want to believe in things that are not true, like you can use disinfectant or horse paste to counteract a virus, or Trump won the 2020 election, despite over 60 lawsuits being thrown out for lack of evidence, and every audit and recount affirming that procedures were sound and confirming prior results; the number of fraudulent votes in the election is less than one-thousandth of a percent, just like every other election. It is not inconceivable that mass-produced fiction will affect elections.

This post was created in Emacs with Org Mode and Love. You’re welcome.

Image from the Vienna Dioscurides, a 6-century manuscript housed at the Austrian National Library (site link here, Wikipedia link here); image from Wikimedia, assumed allowed under public domain.

Email Cerberus and the Three-Body Problem

BLUF: Many people find it difficult to run small, independent email servers due to the behavior of the three largest providers. This post will give some thoughts on email’s three-body problem and on alternatives to Email Cerberus.

Email was originally intended to be a set of decentralized protocols that would have each have multiple implementations, that would each have multiple instances; a hundred implementations would bloom, thousands of servers would provide. Over time, the market of email providers has shrunk, with the Email Cerberus (GMail, Microsoft and Yahoo) having enormous sway over the email landscape. They have the power to reject email from particular domains, and it is hard to get them to reverse their decisions. If you are blacklisted by a provider, you can still get email from other domains, but domains hosted by that provider do not get your emails. Two-way conversations become sources of frustration. This has made it very difficult for small firms and individuals to run their own email servers. Many of them give up and outsource, either to one of the big three, or to a specialized provider.

When you check your email, you are using either POP to get emails from the server to your local machine, or IMAP to read them on an email server. When you send an email, your client sends your message to an SMTP server, which is how servers communicate with each other. For web-based email like Gmail, the web application is just a wrapper around these email protocols.

Over time, more protocols were added to reduce spam, like DomainKeys Identified Mail (DKIM), Sender Policy Framework (SPF) and Domain-based Message Authentication, Reporting and Conformance (DMARC). If your domain gets blacklisted by Email Cerberus, usually it is because you did not set up your DNS entries and email server to conform to these protocols to their satisfaction. One of the more insidious “innovations” by the techbros in Silicon Valley is that is it next to impossible to talk to an actual human to solve your problem. Once the algorithm has decided, your fate is sealed.

If you decide to take your life into your own hands and run your own email server, there are a couple of projects that make it easier to self-host: iRedMail and Mail-in-a-Box (project homepage here, Wikipedia page here). Each protocol is handled by a different application, and there is also a database script to handle the storage of messages. Mail-in-a-Box lists the different components on their Github page. These projects have their own support channels. You can also check out the /r/selfhosted subreddit (self-hosted email is only one topic amongst others on that sub). I do not think either of them can support sending emails from multiple domains.

There are companies that specialize in webmail. Wikipedia has a list here. Based on comments/blogs/toots, some favorites are Fastmail, Hushmail, Protonmail and Tutanota. I think you can use either their web interface or your own client for most of the services on that page. Avoid Mail.ru (now VK) and Yandex.

I found a few others mentioned on the /r/webhosting subreddit: MX Route, Great Mail, 25 Mail St, Roller and Migadu. I have not looked at these too closely, but I assume they are not just front-ends for one of the larger providers. There is a company in Austin called MxToolbox (which is very close to where I live). It looks like they do not provide email hosting. I think they help with setup and monitor your email traffic to ensure your emails are going where they need to go and help you avoid getting blacklisted.

You could also use your shared web host as your email provider. Some of the comments on /r/webhosting were that using your webhost for your email is a bad idea, but I am not a business, and so far it seems to be working out. You can find an incomplete list here.

Sometimes a web host will just outsource email to one of the big three; I think both Dreamhost and Bluehost use Gmail under the hood, and Rackspace uses Office 365.

I use InMotion webhosting. They use Exim based on what I can see in the headers. For a month I have been able to send emails that did not get rejected. They use CPanel, and it had links for all my domains for DKIM and SPF and buttons to fix them. The SPF is there. I did not do anything with DMARC. Yahoo said that the DKIM was unknown. Perhaps InMotion needs to restart their mailserver, or CPanel is not hooked up correctly.

There are a couple of forum sites about email: Email Questions and Email Discussions. They have posts about setting up servers and news and trends in the industry.

2024-11-13: The domain for Email Questions now points to what appears to be a gambling site. I kept the text in the post, but removed the link.

This post was created in Emacs with Org Mode and Love. You’re welcome.

But do not forget this site has a disclaimer.

Image from the Adysh Gospel, a 9th-century Georgian manuscript housed at the Svaneti Museum of History and Ethnography; image from The Gabriel Millet Collection (image page here, collection page here), assumed allowed under public domain.

Trying Out GPT4All

I tried out GPT4All. It is an app that can run an LLM on your desktop. Some of the models it can use allow the output to be used for commercial purposes. The only downside was it is not very fast, and makes my CPU run hot. The project provides installers for Linux, Apple, and Windows (or, as I like to call it: Win-Don’t).

I found out about it while looking into alternatives to ChatGPT and OpenAI. There are a few videos about it on Matthew Berman’s channel, including an interview with the CEO of Nomic AI, the company that started the GPT4All project. I think it is great that people are making alternatives to ChatGPT and OpenAI.

I first downloaded it a couple of weeks ago. The installer itself worked fine, but I had to change the permissions to get it to run: chmod 755 gpt4all-installer-linux.run. I got an error when I tried to run the program: bin/chat: error while loading shared libraries: libxcb-cursor.so.0: cannot open shared object file: No such file or directory. I installed the library as root: apt install libxcb-cursor0. After that it worked fine.

I downloaded the installer again today and re-installed to see if anything was different. I might have still gotten the libxcb-cursor error if I had not installed it earlier. Now the program lets you choose a model when you first run it. Before the model file had to be in the same directory as the executable, and if there was more than one model file it would pick the default. When I tried to run it the first time, I could download a couple of models, but I could not get past that screen. I had to exit and restart the app to actually use the models. There is a list of the models available here (historical commit here). The output of the default model can be used in commercial applications. Most of the other models are based on LLaMA, and commercial use of the output is prohibited.

The new version also gives you the option of sending feedback on the responses back to the GPT4All project.

The model files are from 3.5GB to about 7.5 GB. I have downloaded several model files over the past few weeks, and I have used up as much hard drive space in two weeks as I did in five years on my recently deceased laptop.

While the application is idle it does not use a lot of resources, but when it is generating output the CPU usage and temperature both spike. I do not know if I will be running this on a regular basis. My Meerkat has an Intel i5-10210U CPU, 4.20 GHz, 6 MB Cache, 4 Cores and 8 Threads with 64 GB of memory. The GPU is Intel, so I do not think I will be running a lot of Pytorch.

When I was using the version of the software from two weeks ago and using the ggml-gpt4all-j-v1.2-jazzy model file, I asked it to write some sample sales emails for companies in different industries. It kept writing emails asking for sample sales email instead of an actual sales email. I tried the new ggml-gpt4all-j-v1.3-groovy model (“Current best commercially licensable model based on GPT-J and trained by Nomic AI on the latest curated GPT4All dataset”), and it did better, but the results were pretty similar for each one. They all started with “Exciting news!” and offered a “wide range of” something. I read “Exciting news!” so many times, I started hearing the voice of Professor Farnsworth in my head. Here is the output from the prompt “Write an email from a consulting company whose software developers use Emacs”:

Subject: Exciting news! Our software developers are now using Emacs.

Dear [Recipient’s Name],
I hope this email finds you well. My name is [Your Name] and I am the owner of [Company Name]. We are excited to announce that our software developers are now using Emacs as their primary text editor.

As a company that specializes in software development, we understand the importance of having the best tools for the job. That’s why we offer a wide range of software development tools, including Emacs. Our developers are trained to use the best tools available, and we are always looking for new clients to hire.

If you are interested in learning more about our services, please don’t hesitate to contact us. We would love to discuss your project and see how we can help you achieve your goals.

Thank you for considering [Company Name]. We look forward to hearing from you soon.

I have to agree, Emacs is the best tool for just about any job.

I tried the same prompt with the same model on the new version, and I got this:

Subject: Help with Emacs – Our Developers Need Assistance

Dear [Name],

I hope this email finds you well. We are writing to inquire about your experience using the popular text editor, Emacs. As we understand that you work as a developer for our esteemed client, we would like to offer some assistance in resolving any issues you may be facing while working on their projects.

We have noticed that many of our clients often struggle with certain aspects of using Emacs, such as navigating through files efficiently, finding specific lines of code, or even understanding basic syntax. These challenges can lead to frustration when trying to accomplish tasks within the program.

To help alleviate these frustrations, we would love to invite you to participate in a webinar where we will provide step-by-step instructions on how to effectively navigate and utilize the features of Emacs. This webinar will cover topics such as file navigation, search functions, and programming language support.

If you are interested in participating, please let us know by responding to this email. If not, no need to worry, we completely understand if you prefer to continue using another tool. However, we believe that our webinar could greatly benefit you and your team members who also work on projects involving Emacs.

Thank you for considering our invitation. We look forward to hearing back from you soon.

Best regards,
[Your Name]

Not as positive. I selected the option to regenerate output, and it was just one sentence that did not even mention Emacs.

I tried the prompt with the model ggml-gpt4all-l13b-snoozy, which is “Current best non-commercially licensable model based on Llama 13b and trained by Nomic AI on the latest curated GPT4All dataset”, and I got this:

Subject: Emacs Software Development at [Consulting Company]

Dear [Client],

I hope this email finds you well. My name is [Your Name], and I am the Director of Software Development at [Consulting Company]. I am writing to express our excitement about the opportunity to work with you on your upcoming project.

At [Consulting Company], we pride ourselves on our use of Emacs as our primary text editor. Our developers have found that Emacs provides a powerful and flexible platform for software development, and we believe that it will be an asset to your project as well.

We understand that your project is of the utmost importance, and we are committed to delivering a high-quality solution that meets your needs. Our team of experienced developers will work closely with your team to ensure that your project is completed on time and within budget.

If you have any questions or concerns, please do not hesitate to reach out to us. We look forward to working with you and delivering a successful project.

Best regards,

[Your Name]
Director of Software Development
[Consulting Company]

I agree that “Emacs provides a powerful and flexible platform for software development”, and that it is as asset to any project.

Some of the responses interpreted the prompt to produce what I want (“We are amazing and Emacs is amazing and we can make amazing software for you with Emacs”) and one was offering to teach developers how to use Emacs. I tried a couple of more times, and again the results were different with the same prompt.

There is a project that provides a command line interface to the models called LlamaGPTJ-chat, which I might get to in a few more days. I have tried a couple of other CLI programs, and they also use a LOT of CPU.

Michael Finney wrote about his experience with GPT4All here. Per his article, GPT4All will soon have an API that can be called and run on a local server. I joined the GPT4All discord, but I have not spent too much time there.

I do not know if I will be using this too much since it puts a lot of stress on my system. I will see if anyone is running models on the cloud and find out how much it costs. But I support anyone who will give the world alternatives to OpenAI.

You’re welcome.

I am not using an image of someone writing because while this post mentions Emacs, it is not about Emacs.

Image from Madrid Skylitzes, a 12th-century manuscript of Synopsis of Histories by Greek historian John Skylitzes; manuscript housed at National Library of Spain; manuscript information here. Image from World Document Library, licensed under CC BY-NC-SA 4.0.

Reactions To Recent AI Developments

There are a variety of reactions to what has been happening in AI lately.

Some of them are depressing. All of the people who will not say the sun comes up until Microsoft Teams or Office says it does are doing the same thing with AI. They think it is all about OpenAI and ChapGPT. MS owns part of OpenAI. LLMs are one of the biggest changes to come along in technology in a decade. We should not pass up this chance to prevent the company that has made technology suck for the past three decades have any more influence in our lives or society [Note 1].

Maybe I should not be shocked by the immensity of human stupidity, but I do not understand why people are not grabbing this chance to get Microsoft out of our lives with both hands.

I see this a lot at my employer [Note 2]. There the unholy trinity is OpenAI, ChatGPT, and Microsoft Azure. There are a lot of higher-ups who just push whatever garbage vendors are trying to sell, regardless of whether or not any of the companies that we are supposed to be helping (aka “paying clients”) want any of it. I do not understand why these people are paid lots of money to look at “new technology”, only to turn around and say “More Oracle! More SAP! More Microsoft!” A few years ago, a lot of them were pushing blockchain, which has gone nowhere. And none of these block-chumps admit that they were wrong about that. [Note 3]

Now these shysters are pushing the metaverse, even though only literally the only person on the planet who wants it is Mark Snakerberg. One reason I think he wants it is if they can get you to go to their site with their gear, then they own the whole experience. Right now you have to use a browser on a PC or an app on a phone to use Facehook, and Meta does not own an OS or a browswer. But that is not anybody else’s problem. I think the other reason is that he is so stiff and robotic he is the only person who has an avatar that looks more human than he does, even without legs. Here is an article and discussion on Slashdot about retailers dumping the metaverse. I can see retailers using the metaverse: people might want to try out new clothes without actually changing several times. If retailers and Disney do not want the metaverse, then it really is dead. I have heard the headsets are too heavy. Do you want something wireless surrounding your entire head? (Hey, maybe that’s why Snakerberg keeps throwing money at something nobody else wants.)

I wonder if he is regretting giving Sandberg the boot.

Even the Emacs community is gung-ho about OpenAI and their products. I noticed that Sacha Chua started a section for AI in her weekly Emacs news posts. The first AI mention I could find was for a GPT package on 2022-11-21 (as of 2023-04-24, it looks like that package is for OpenAI models only). The section started showing up on 2022-12-12, on and off until February, and then consistently since then. There are a few packages that say they will incorporate other LLMs as more are available. Most of the AI packages are just more wrappers around ChatGPT. There are a few posts on the subreddit asking about open source alternatives, and one about OpenAssistant (which I mention below). The posts are here, here (with a response from Irreal here) and here. I know that OpenAI’s products are more mature than others at the moment, but it seems like even the open source crowd is going all-in on the billion-ai-res’ shiny object. [Note 4]

It will be interesting how the corrupt-o-currency crowd reacts to AI. A lot of people insisted they were in corrupt-o-currency “for the technology”. I think AI will show us if those people are really interested in technology, or if they are stupid, or grifters. Now we have something that really IS interesting. Corrupt-o-currency tech was not that interesting. Increment a number going into a hash function until your output starts with a particular number of zeroes. That is really it. Immutability is not the default in a lot of programming languages, and it can make data easier to manage and reason about, but it is not unique to corrupt-o-currency. And it’s a pretty dumb reason to fool yourself.

Blockchain was a stalking horse for bitcon that promised a glorious future that never arrived. Every time I watched or watched anything about blockchain, it was always vendors pushing products, and there were never any user testimonials. Contrast that with AI: with AI we see users actually trying it out. There are a lot of people talking about AI who are not trying to sell you something, while blockchain/NFT/bitcon was nothing but grifting. We do not see vendors going on about “someday”, or AI bros saying, “Have fun staying dumb.” We do see people implementing models to compete with ChatGPT (more on that below).

A lot of corrupt-o-currency advocates say it’s still early days for their magic beans. They have been saying that for ten years, and there have been a LOT of people pushing it and trying to find a use for it. They might counter this is not the first time people thought AI was going to change the world, and there were a couple of AI winters: 1974–1980 and 1987–1993, so could there be a corrupt-o-currency winter as well? One difference is that nobody went to jail for AI fraud. There were no “rug pulls” in AI back in the day. AI has been attempts at solutions that until recently never solved the problems they were trying to solve. Block-coin was a technology looking for a problem to solve (besides money laundering and selling drugs). The whole concept of “digital assets” makes no sense at all. They do not have income streams like companies do, and they have no intrinsic use beyond financial transactions like commodities do. It is like someone found a way to combine Beanie Babies and coin flips and people decided to start gambling on them. Aside from helping pedophiles launder North Korean drug money, none of this serves any purpose.

AI has been around for a while, but I think that it was the release of Chap-GPT 3.5 in November that really changed things. The first time we talked about ChatGPT at the EmacsATX meetings was in December.

Right now AI and LLMs are en fuego. Not everyone is just pushing incumbent vendors and their products. There are a lot of projects working on open source LLMs. I wrote about the idea that people might try to run LLMs locally, and there are projects working on that.

Some of them are based on Facebook’s LLaMA model, so they cannot be used for commercial purposes. One is llama.cpp. There is one in Golang called llama.go. Another in Golang called LocalAI. One goal of these projects is to be able to be run on a CPU, instead of needing a large cluster of GPUs. There is also a subreddit dedicated to running LLaMA locally. Some experts think that making models with more parameters is not the way to make progress, and that algorithms are where progress will occur. Will this mean that the need for GPUs will plateau, and CPUs will become more important in AI? I don’t know. I tried using some model files from Hugging Face that are based on LLaMA, like Stanford’s Alpaca, but so far I have not been able to get anything working with that one. I think it only works with the original LLaMA files.

There is a project called Red Pajama from Together.xyz to build a completely open source counterpart to the LLaMA model. Their plan is to have something that can be run on a consumer grade GPU.

An interesting project is GPT4All. This project can be run locally. It is made by a company called Nomic.AI; their main product makes visual representations of AI datasets. I found out about it from Matthew Berman’s Youtube channel. It is uses an older model from EleutherAI called GPT-J (Hugging Face page here, Wikipedia page here). I am part of the Discord for this project, so I will keep an eye on this. The CEO of Nomic was interviewed by Matthew Berman, and he talked about how they went through a lot of effort to get a dataset that is completely open source and can be used for research and/or commercial purposes. He said that he thinks OpenAI has a lot of proprietary data in their dataset, partially due to how they created it, partially due to people uploading their own internal data into it. He predicts there will be a lot of lawsuits over AI data for years.

I got GPT4All to work locally, but I did get an error the first time: /bin/chat: error while loading shared libraries: libxcb-cursor.so.0: cannot open shared object file: No such file or directory. A Google search led me to an issue on their Github repo. I did not need to run all those commands, I only needed to run this one: apt install libxcb-cursor0.

Another project is OpenAssistant, made by a non-profit in Germany called LAION (Large-scale Artificial Intelligence Open Network, pronounced like the large feline “lion”) (Github page here, Hugging Face page here). I think the goal is not only to use their dataset to respond to prompts, but to be able to retrieve external data (like Google search results) in real-time. They want to release something that can be run locally, but they want the main interface to be their website. One of the ways the developed their dataset is by having humans ask questions, other people answer questions, and still more people rate the questions and the answers; someone could perform all three roles for different questions.

I found out about this from the YouTube channel of one of the leaders of the project named Yannic Kilcher. At first I did not think he was a real person but an AI-generated avatar because there are some jumps and jerks in his speech. Then I looked at a few live streams, and I think that for his videos he edits out all the filler words and pauses. I am in their Discord and will keep an eye on this.

Another open source project is StableLM from Stable.AI. They made the image generator Stable Diffusion. I thought they were all about images until their LLM came out. Right now I do not know too much about this one.

One possible clue to what might happen is a science fiction project called Orion’s Arm (site here, Wikipedia page here). Unlike most sci-fi, there are no humanoid aliens. There are baseline humans, humans augmented with various levels of technology, and AIs of various levels, up to almost god-like omniscience. Some people have put some thought into how humans will live with AI beyond just “AI will kill us all.” Interestingly, some of the images on that site are made with the Midjourney AI.

One thing about all of this that depresses me is I never got the chance to work with technologies that really interest me. When I read about or talk to Lisp and Smalltalk developers of yore, it seems like there was an age of heroes when gods and men strode the earth together. Now the world is full of pinheads pushing vendor nonsense, and people too stupid and too lazy to use something not made by Microsoft. Let’s get this company out of our lives forever.

Another thing that depresses me is I bet all the alternative medicine woo-woo people are not worried about their jobs.

You’re welcome.


Note 1: If you think OpenAI is actually open because it has the word “open” in it, you are probably the same sort of person who thinks SharePoint is good for sharing because it has the word “share” in it. Do us all a favor: go sit in a rocking chair right now and get all your nutrients through a straw. You are about as useful to society as someone already in that state.

Note 2: I do not speak for my employer, nor do they endorse this blog, as per the About page. Per the Disclaimer page: if any of this bothers you, go jump off a cliff.

Note 3: I don’t want anybody to think I have nothing good to say about my employer. Granted, I am currently making a living with a proprietary software tool that I really do not like. When I joined I knew nothing about it, and they paid me while they trained me how to use it. A lot of companies would never do that. And there were a couple of times in the past decade when someone found something else for me to do while things were slow. It is a big company. I think it has more than half a million employees. There are some smart people there, and frankly some dumb ones. It seems like too many decisions are made for the wrong reasons: inertia, the sunk cost fallacy, and this-is-what-the-vendor-told-us-to-do. Why can’t these companies founded by billionaires do their own marketing?

The company does actually do some useful things. They do a lot of work for various governments and large corporations around the world. Entities that actually make products and services people use. Maybe startups have more interesting technology, but I never bought into the whole startup religion here in Austin. There are a lot of people who seem to want to work for startups just because they are startups. Asking them what their company does or why anyone should care is a question a lot of them do not seem to like and sometimes do not have an answer for. There were a few people that I would see at some meetups maybe once a year. Every time I saw them they were at a different company that I had never heard of. They all just seemed to fade away. Maybe they made more money than me, but what are they really doing for the world? If any of those companies are still around, they are just leaving messes for someone else to clean up.

Note 4: I do not want any of this to be interpreted as disparaging Sacha Chua or all the things she does for the Emacs community. I think her posts simply reflect what is happening in the Emacs community. There are some people in the Emacs community who are as leery of AI consolidation as I am.

Image from The Codex of Fernando I and Doña Sancha, aka Beatus Facundus, an 11th-century manuscript manuscript of ‘Commentary on the Apocalypse‘, written in the 8th century by Beatus of Liébana; manuscript created at monastery of Santo Toribio de Liébana (Wikipedia page here), currently housed at the National Library of Spain; manuscript information here. Image from World Document Library, licensed under CC BY-NC-SA 4.0.

Notes on LLMs and AI

There has been a lot of press lately about AI, OpenAI, GPT-${X}, and how AI will affect the world. For the time being, I plan on not looking further into AI (unless my current or a future employer compels me to). I think that right now there is not enough diversity in the vendor population. I also have some thoughts on how it will affect things going forward.

I do not like to get too meta in my posts, but I have been writing this on and off for over a week, and I want to get it out before too much more time passes. I am still learning about this stuff, like what is the difference between models, weights, and datasets; some articles use a project name to refer to all three components. The LLaMA debacle is the textbook case: some stuff was released, some was leaked, there are projects that are based on Meta’s work, some that seem to be clean-room implementations, so how it all fits together is murky to me.

GPT-${X} by OpenAI is taking the world by storm, particularly Chat-GPT. It was the focus of a recent EmacsATX meeting. It is disruptive in the sense that it has capabilities beyond prior AI technology, and will probably have a profound affect on society going forward. But in another sense, it is the opposite of disruptive; it consolidates power and influence in OpenAI. One of the owners of OpenAI is Microsoft, and for me that makes anything by OpenAI something to avoid. They are not doing this for you.

I think a lot of people do not realize that when they play around with the OpenAI prompts in ChapGPT, they are training the OpenAI models and making them better and more powerful. Power that can be used by other users of tool. Not only the vendors, but also your competitors. There have been reports of confidential data and PII being put into ChatGPT, and then extracted by other users later. People need to be more careful. And stop making the rich and powerful richer and more powerful. A lot of people in corporate America might work at companies that are independent on paper, yet they all act like they want to be subsidiaries of Microsoft. Start looking out for your own company and your own career and your own life.

The GPT-${X} products were used in making GitHub Copilot. I mentioned Copilot when I posted I was moving from Github to Codeberg. It does not respect licenses, which could put a company at legal risk, and sometimes it “solves” a problem while violating stated constraints. GPT-${X} has the same issues: Who owns the training data? Who owns the output?

It is good to automate things, but could relying on AI too much make people stupider? A good point was brought up in the discussion about why MIT dropped SICP: When you rely on a black box, how do you know you can rely on the black box? I think we might be coming close to fulfilling a prophecy from Dune:

Once men turned their thinking over to machines in the hope that this would set them free. But that only permitted other men with machines to enslave them.

I think we should collectively make an effort to avoid anything by OpenAI, and anything Microsoft. I do not know how long Microsoft has been involved with OpenAI, but there are a few MS hallmarks: it is called “OpenAI” even though it is not open (they have been tight-lipped about how they trained their data), and when it is wrong it insists you are wrong. And when it is incorporated into MS products it has started pushing ads.

There are a few alternatives out there. There is a company called Hugging Face that I think provides datasets, different models and hosting for AI. I think you can provide your own data. There is a company called Lambda Labs which provides all your AI/GPU needs: cloud hosting, colocation, servers, workstations with terrabytes of memory, and a very expensive and very nice looking laptop with Ubuntu pre-installed (a LOT more than System76, but it is nice to see more Linux laptops out there).

WRT software, there are some implementations of AI that are open source. NanoGPT can be run on a local system, although it might take a while. You can find the Github link here, and a link to what might be a fork on Codeberg here. It was started by Andrej Karpathy, who worked on autonomous driving at Tesla and worked at OpenAI.

GPT is a type of artificial neural network known as a large language model, or LLM. Then Facehook/Meta released an LLM called Large Language Model Meta AI, or LLaMA, so now there are a few projects with names referring to South American camelids: llama.cpp (Github link here, Hacker News discussion here), and a fork of llama.cpp called alpaca.cpp (Github link here, Codeberg link here). Once they saw money going to someone else’s pockets, Stanford decided to get in on the act with their own LLaMA implementation, also called Alpaca. There is one caleld Vicuna (intro page here, Github link here). And, last but not least, Guanaco, which look like a fork of Stanford’s Alpaca (Github repos here, page here). You would think AI researchers would come up with more original names rather than run a theme into the ground.

Note: I think Facebook/Meta did release some papers about LLaMA, and then some parts of it were leaked. The status of these projects is a bit unclear to me at the moment. Some of the projects mentioned cannot be used for commercial purposes. IANAL, but I think that llama.cpp and alpaca.cpp can since they are clean-room implementations and were not created with any assistance or collaboration with Meta. Stanford got some early access to LLaMA, so its project and Vicuna cannot be used for commercial purposes.

You can find some more info about open source AI here on Medium (archive here), and here on HN. I think the group EleutherAI is trying to be an open source counter to OpenAI.

There are a LOT of other AI projects out there, but a lot of them are just interfaces to Chat-GPT or DALL-E or something else from OpenAI, as opposed to a program you can run for yourself. A lot of the forks and clean-room/non-OpenAI models require a LOT of memory. Some need at least 60 GB. The mini I got from System76 can have up to 64GB. They have desktops that can go up to 1TB of memory, and servers up to 8TB. Granted, maybe something local will never catch up to OpenAI, but as a few comments in the HN discussion on llama.cpp pointed out: the open source models are becoming very efficient very quickly. Granted, some of the commenters said that AI might be out-of-reach for the hobbyist. But then all this stuff is doing is simulating a human.

So where does all this go next? Honestly, who knows, but I will share my thoughts anyway.

First off: I dismiss the doomsday scenario that AI will kill us all. Like the Wikipedia page on “pessimism porn” states: A lot of people like to predict disaster because it makes them feel smart, even if years go by and their predictions never come to pass. There are lot of people with blogs and YouTube channels that are always predicting a stock market collapse, or who think we are about to become Weimar Germany if the price of a gallon of milk goes up one cent. They dismiss you if you cannot offer irrefutable proof that the world will NOT end, yet they insist their predictions are to be regarded as self-evident. Granted, maybe those are not the best arguments against Skynet, but I have dealt with a lot of people who confuse the strength of their convictions for logic. Sometimes the best prediction is that things will mostly continue as they are, just with more of something you do (or do not) like.

Since this will be a major change, there will be an effect on jobs. Some jobs will be lost. But there might actually be more jobs due to AI. Scott McNeally pointed out that making a system used to be a masters thesis, and systems were pretty limited. Now we have powerful software that is easy to install. We have packages (like JDK, Golang, Elixir) that are powerful compilers and runtimes, far beyond what people thought was possible a few decades ago, yet they can be downloaded as tar or zip files that once expanded let people create robust, powerful software. Linux and these VMs have created a lot of technology jobs. I think AI might wind up on net creating more than we have now.

Granted, it is possible that the jobs that get created are more soul-sucking than what we have. I joked on Mastodon that AI will not take your job; it will just take away the parts you like, leaving you with the parts you do not like.

There is one group of people that I do hope loses their jobs: all the More Bad Advice pinheads [Note 1] who all sound the same and think the answer to everything is to cut costs. I have had good and bad bosses, but honestly, a lot of people in Corporate America sound the same: asking when things will be done, going on and on about how important some arbitrary deadline they pulled out of thin air is, harping on about innovation yet only having the same tired ideas (piling on more work during the so-called “good times”, then cutting staff when things start looking shaky).

And there will be more people thinking the same. One thing that really grates on me is that we are told in technology that we have to be constantly learning new things. Yet the world is full of business pinheads who cannot conceive of not using Excel. And there are plenty of software developers who cannot conceive of doing something in a language that is not Javascript. I have a bad feeling that OpenAI will become the Third Pillar of Technology Stupidity.

Sadly, maybe that will be the way to stay employed. Be a Microsoft drone, a Javascript drone, or an OpenAI drone. I have met tech people older than me who said they could do things decades ago with Lisp and Smalltalk that most languages and runtimes still cannot match. I feel like we took a wrong turn somewhere.

That said, even if AI leads to more jobs, there could still be downsides. We are already seeing this: Generative AI is already being used to craft more effective phishing emails. ChatGPT accused a law professor of sexual harassment (article here, HN discussion here). The HN comments have examples of AI making stuff up, but the professor gave a good summary: “ChatGPT falsely reported on a claim of sexual harassment that was never made against me on a trip that never occurred while I was on a faculty where I never taught. ChapGPT relied on a cited Post article that was never written and quotes a statement that was never made by the newspaper.” What if this is used for background checks and nobody verifies what the AI tells them? This could cause a lot of damage to people. Per the quote misattributed to Mark Twain, a lie can travel halfway around the world before the truth can get its boots on.

We should call AI “artificial inference”, because it mostly makes up stuff that sounds true. It just makes guesses about what seems logical. For a long time it was logical to think the earth is flat. Yet for some reason people think the output of AI is always true. Perhaps they are assuming that it must be true since it is based on large data and advanced technology. But sometimes the machine learning is just machine lying. Marissa Mayer said Google search results seem worse because web is worse (articles here and here). People used to put content on the web to tell you things, and now they just want to sell you things. There is lots of junk on the web. I predict here will be a lot of junk in AI.

Microsoft is putting ads in Bing AI chat which is already fostering distrust in AI (article here and here). Unlike Google search ads, the ads in the chat are hard to distinguish from the rest of the results. If companies need to put ads in AI, then make it like Google ads. People realize that things need to be paid for. Intermingling ads with AI just ruins the AI. You do not need advanced AI to say something you are getting paid to say. Google has been able to serve ads based on user input since 2004.

I think AI will lead to a lot of artificial and misleading content. Not just text, but also audio and video. People might not be able to believe what they read, see or hear online. It could cause more cynicism and distrust in our society. Perhaps we will not get Skynet, just a slow decay and further fracturing of society.

AI could, of course, lead to massive job losses. A lot of people care more about cost than quality. And it is possible that after a time some of those jobs might come back. There is a post on Reddit (link here, HN discussion here) about a freelance writer who lost a gig to ChatGPT. (Another writer wrote an “AI survival guide“.) A few comments gave anecdotes of multiple applications to jobs that all sounded the same that the HR people realized were all done with AI. If more companies start using AI, a lot of websites will all start to be the same. A lot of people hate it when an article “sounds/feels like it was written by AI”. Perhaps the human touch will make a comeback. There is a joke I read somewhere:

An LLM walks into a bar.
The bartender asks, "What will you have?"
The LLM says, "That depends. What is everyone else having?"

Granted, it might be a while before jobs lost to AI come back, assuming they ever do. And not all of the jobs might not come back.

I think that people who understand concepts will do better in the long run than people who just know a tool. At least, that is how things have been. It could be different this time. On the other hand, could an AI come up with “Artisanal Bitcoin“?

Software used to be done in binary or assembly, and over time the languages became more powerful, and the number of jobs increased. Software was always about automation, and there was always something to automate. Has that cycle stopped?

I am worried, but I cannot just yet get on board the Doom Train. I remember working at Bank of America in the 00s/Aughts/Whatever that decade is called, and we all thought that all our jobs would go to India and there would be No More Software Made In ‘Merica. That did not happen.

Or maybe it is all a bubble that will burst.

Maybe the AI is not as advanced as the companies are telling us. OpenAI does not publicize it, but they used people in Kenya to filter the bad stuff (Reddit discussions here, here and here, Time article here with archive here, Vice article here with archive here, another Vice article here with archive here). One major focus of the articles is that looking at all the toxic content was so traumatic for the workers that the company that got the contract ended it several months early. Looking at toxic content can wear on people. But isn’t the point of an AI to figure this stuff out?

My employer had us watch some videos on up and coming technology, and one of them was on AI. One of the people on the panel kept taking about how important it is to “train” and “curate” your data. They kept saying that over and over. And I had the same thought: isn’t that what the AI is supposed to do? They made it sound like AI was just a big fancy grep or SQL query.

Per the Vice articles, tech and social media companies have been using people in low-wage countries to flag content for years, while letting people think that their technology was so amazing. Perhaps ChatGPT is no different. I do not know if they have to re-flag everything for each version of GPT. I get the impression the data is trained when the AI is started up, and from there it is just repeating what it figured out. Does it actually learn in real-time the way a human can? Can an AI make new inferences and be an old dog learning new tricks the way a human can, or does it just keep re-inforcing the same ideas and positions the longer it runs? What if you train your data and the world changes? What if neo-nazis stop using triple parentheses as an anti-Jewish code today, and your training data is from two years ago? I guess you are just back to web search.

I think part of what is going on is hype. As Charlie Stross pointed out, it does seem interesting that we see the AI hype just starting as the corrupt-o-currency hype is winding down. The vulture capitalists need something new to sell.

Another issue is: will this scale going forward? Technology does not always progress at the same rate. We could be headed for another AI winter. Research into AI for autonomous driving has hit a wall (no pun intended).

And how will this scale? The human brain still have 1000 times the number of connections as GPT-4 has parameters. There is already a shortage forming for the chips used for AI. Is it worth it to burn the planet and use all that metal and electricity to chew through a lot of data…to do what? Simulate a human brain in a world with 8 billion people? Especially when a lot of the humans’ intelligence is not being used efficiently (see penetration of Lisp vs Windows).

That said, I don’t think AI will go away. If I could have one thing, I would like to see alternatives to OpenAI, particularly open source. It might be possible to run LLMs locally. Do you really need an AI that knows about oceanography? Most of us do not. I do not think that AI will kill us all (it is not clear to me how we go from chatbot to Terminator). But corporate consolidation in AI would be a tragedy.

I just need a job where I can use Emacs and call people stupid.

You’re welcome.


Note 1: Not all More Bad Advice pinheads actually have a More Bad Advice degree. However, the ideas pushed by the More Bad Advice crowd are pretty much everywhere.

Image from an 11th-century manuscript housed in the Topkapi Palace in Istanbul, image from The Gabriel Millet Collection (collection page here), assumed allowed under public domain.

Making An Elixir Project

Now I will go over making an Elixir project. This is a continuation of my post about learning project structure and testing from the beginning when learning a new programming language.

Elixir took a bit more work. I made a project and I thought I was doing it correctly, but after a certain point every time I ran the tests it ran the app instead. I could not figure out why. So I started over. I followed along with a project in Dave Thomas’s Elixir book. He does not start a project until Chapter 13, which I think is odd. Why not start a project from the beginning?

Right now I do not know a whole lot about Elixir or the Elixir community or ecosystem, so this post might contain some opinions and speculations that will seem $INSERT_NEGATIVE_TERM to Elixir experts.

You can install Elixir with the asdf tool. It should manage dependencies for Elixir itself, but not your Elixir projects; Elixir requires another language named Erlang to be installed. Check the asdf Getting Started page to download and install it.

After you install asdf, you need to install the Erlang and Elixir plugins, and then install Erlang and Elixir themselves.


asdf
asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git
asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git
asdf plugin list
asdf install erlang latest
asdf install elixir latest
asdf list
asdf list elixir
asdf list-all erlang
asdf list-all elixir

The tool to manage Elixir projects and dependecies is called Mix. To list all the commands, use “mix help”. You can find out more here and here. It is to Elixir what Maven or Gradle is to Java, or Leiningen is to Clojure. I think it is more like Gradle or Leiningen than Maven, because I think that it is easier to add functionality to Mix that it is to Maven, and it is easier to add functionality to Gradle and Leiningen than Maven. I think the Phoenix web framework adds some Mix tasks. My installation of Elixir and Mix has some Phoenix tasks built-in. I do not know if that is because whoever made the asdf package included them, or if they are part of all Elixir installations. I would be a bit surprised if the Elixir maintainers would include Phoenix and play favorites.

First make a directory for Elixir projects.


ericm@latitude:~$ mkdir elixir.projects
ericm@latitude:~$ cd elixir.projects/
ericm@latitude:~/elixir.projects$ 

Next run Mix to make a new project


ericm@latitude:~$ cd elixir.projects/
ericm@latitude:~/elixir.projects$ mix new foothold
\* creating README.md
- creating .formatter.exs
- creating .gitignore
- creating mix.exs
- creating lib
- creating lib/foothold.ex
- creating test
- creating test/test_helper.exs
- creating test/foothold_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd foothold
    mix test

Run "mix help" for more commands.
ericm@latitude:~/elixir.projects$ cd foothold/
ericm@latitude:~/elixir.projects/foothold$ ls
lib/  mix.exs  README.md  test/

Elixir uses modules instead of classes, and they are in namespaces. I want to make one for my project called “foothold”. I ran “mix help”, but none of the task summaries looked like what I want, so we have to go old-school and do this by hand. I am not sure if Elixir calls them “namespaces”, but that is how I think of them.


ericm@latitude:~/elixir.projects/foothold$ mkdir lib/foothold
ericm@latitude:~/elixir.projects/foothold$ mkdir test/foothold

As with our golang project, make a package (or namespace, or prefix) for some modules that we will write.


ericm@latitude:~/elixir.projects/foothold$ mkdir lib/more_strings
ericm@latitude:~/elixir.projects/foothold$ mkdir test/more_strings

We will make a couple of files that make duplicates of strings and reverse strings, and we will include some tests for them. The modules will have the “ex” extension. The tests will have the “exs” extension because they are scripts and if we compile our app the tests would not be included.

Make a file lib/more_strings/duplicate.ex:


defmodule MoreStrings.Duplicate do

  def duplicate_string(arg_string) do
    String.duplicate(arg_string, 2)
  end

  def make_three_copies(arg_string) do
    String.duplicate(arg_string, 3)
  end

end

Make a file test/more_strings/duplicate_test.exs:


defmodule MoreStrings.DuplicateTest do
  use ExUnit.Case          # bring in the test functionality
  import ExUnit.CaptureIO  # And allow us to capture stuff sent to stdout
  doctest MoreStrings.Duplicate
  alias MoreStrings.Duplicate, as: MSD

  test "try duplicate_string" do
    assert "andand" == MSD.duplicate_string( "and" )
    refute "andanda" == MSD.duplicate_string( "and" )
  end

  test "try make_three_copies" do
    IO.puts "In the test for make_three_copies"
    assert "zxcvzxcvzxcv" == MSD.make_three_copies( "zxcv" )
  end
end

Make lib/more_strings/reverse.ex:


defmodule MoreStrings.Reverse do

  def reverse_stuff do
    IO.puts "In MoreStrings.Reverse"
  end

  # why doesn't it like this?
  def actually_reverse_string(arg_string) do
    IO.puts "In MoreStrings.actually_reverse_string with arg #{arg_string}"
    IO.puts String.reverse(arg_string)
    String.reverse(arg_string)
  end

  def revv(arg_string) do
    IO.puts "In MoreStrings.Reverse.revv with arg #{arg_string}"
     IO.puts String.reverse(arg_string)
  end
end

Make test/more_strings/reverse_test.exs


defmodule MoreStrings.ReverseTest do
  use ExUnit.Case          # bring in the test functionality
  import MoreStrings.Reverse
  import ExUnit.CaptureIO  # And allow us to capture stuff sent to stdout

  # alias MoreStrings.Reverse, as: MSR
  # import MoreStrings.Reverse

  test "try reverse" do
    IO.puts "In the test try reverse"
    # assert "dolleh" == MSR.actually_reverse_string( "ahello" )
    assert MoreStrings.Reverse.actually_reverse_string("ahello") == "olleha"
    refute actually_reverse_string( "hello" ) == "dollehd"
  end

  test "ttttttt" do
    IO.puts "In test tttttt"
    assert 4 == 2 + 2
  end

end

Now compile the app with “mix compile” and run the tests with “mix test –trace”. Adding the –trace will print a message to the console for each test being run even if you do not have any IO.puts statements.


ericm@latitude:~/elixir.projects/foothold$ mix compile
Compiling 3 files (.ex)
Generated foothold app
ericm@latitude:~/elixir.projects/foothold$ mix test --trace
Compiling 3 files (.ex)
Generated foothold app
warning: unused import ExUnit.CaptureIO
  test/more_strings/reverse_test.exs:4

warning: unused import ExUnit.CaptureIO
  test/more_strings/duplicate_test.exs:3


MoreStrings.DuplicateTest [test/more_strings/duplicate_test.exs]
  * test try duplicate_string (0.02ms) [L#7]
  * test try make_three_copies [L#12]In the test for make_three_copies
  * test try make_three_copies (0.03ms) [L#12]

FootholdTest [test/foothold_test.exs]
  * doctest Foothold.hello/0 (1) (0.00ms) [L#3]
  * test greets the world (0.00ms) [L#5]
In the test try reverse

MoreStrings.ReverseTest [test/more_strings/reverse_test.exs]
In MoreStrings.actually_reverse_string with arg ahello
  * test try reverse [L#9]olleha
In MoreStrings.actually_reverse_string with arg hello
ollehericm@latitude:~/elixir.projects/foothold$ mix compile
Compiling 3 files (.ex)
Generated foothold app
ericm@latitude:~/elixir.projects/foothold$ mix test --trace
Compiling 3 files (.ex)
Generated foothold app
warning: unused import ExUnit.CaptureIO
  test/more_strings/reverse_test.exs:4

warning: unused import ExUnit.CaptureIO
  test/more_strings/duplicate_test.exs:3


MoreStrings.DuplicateTest [test/more_strings/duplicate_test.exs]
  * test try duplicate_string (0.02ms) [L#7]
  * test try make_three_copies [L#12]In the test for make_three_copies
  * test try make_three_copies (0.03ms) [L#12]

FootholdTest [test/foothold_test.exs]
  * doctest Foothold.hello/0 (1) (0.00ms) [L#3]
  * test greets the world (0.00ms) [L#5]
In the test try reverse

MoreStrings.ReverseTest [test/more_strings/reverse_test.exs]
In MoreStrings.actually_reverse_string with arg ahello
  * test try reverse [L#9]olleha
In MoreStrings.actually_reverse_string with arg hello
olleh
  * test try reverse (0.1ms) [L#9]
  * test ttttttt [L#16]In test tttttt
  * test ttttttt (0.02ms) [L#16]


Finished in 0.1 seconds (0.00s async, 0.1s sync)
1 doctest, 5 tests, 0 failures

Randomized with seed 154594

  * test try reverse (0.1ms) [L#9]
  * test ttttttt [L#16]In test tttttt
  * test ttttttt (0.02ms) [L#16]

Finished in 0.1 seconds (0.00s async, 0.1s sync)
1 doctest, 5 tests, 0 failures

Randomized with seed 154594

Run “iex -S mix” in the root of your project to use your modules. IEx is the interactive Elixir shell that comes with Elixir. You can type in Elixir code and get results. It is sort of like un-automated unit tests. You can end the session by hitting Control-C (or as we say in Emacs land: C-c) and then “a” and the return key.


ericm@latitude:~/elixir.projects/foothold$ iex -S mix
Erlang/OTP 25 [erts-13.0.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.13.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> MoreStrings.Reverse.actually_reverse_string("ahello")
In MoreStrings.actually_reverse_string with arg ahello
olleha
"olleha"
iex(2)> alias MoreStrings.Duplicate, as: MSD
MoreStrings.Duplicate
iex(3)> MSD.duplicate_string( "and" )
"andand"
iex(4)> MSD.make_three_copies( "zxcv" )
"zxcvzxcvzxcv"
iex(5)> 
BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo
       (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution

Now add an external dependency to the project. The package we will add is Decimal, a package for arbitrary precision decimal artithmatic (Hex page here, documentation here, Github repo here). First we need to add a reference to it in our mix.exs file in the “defp deps” section:


defp deps do
  [
    {:decimal, "~> 2.0"}
    # {:dep_from_hexpm, "~> 0.3.0"},
    # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
  ]
end

Here are the Mix tasks associated with dependencies:


mix deps              # Lists dependencies and their status
mix deps.clean        # Deletes the given dependencies' files
mix deps.compile      # Compiles dependencies
mix deps.get          # Gets all out of date dependencies
mix deps.tree         # Prints the dependency tree
mix deps.unlock       # Unlocks the given dependencies
mix deps.update       # Updates the given dependencies

Run “mix deps.get” to fetch the dependencies and “mix deps.compile” if it makes you feel better:


ericm@latitude:~/elixir.projects/foothold$ mix deps.get
Resolving Hex dependencies...
Resolution completed in 0.033s
New:
  decimal 2.0.0
\* Getting decimal (Hex package)
ericm@latitude:~/elixir.projects/foothold$ mix deps.compile
==> decimal
Compiling 4 files (.ex)
Generated decimal app
ericm@latitude:~/elixir.projects/foothold$ mix deps
\* decimal 2.0.0 (Hex package) (mix)
  locked at 2.0.0 (decimal) 34666e9c
  ok

Add a module that depends on Decimal in lib/foothold/decimal_stuff.ex, and make a few calls so we have something to test:


defmodule Foothold.DecimalStuff do
  def do_decimal_add(a, b) do
    Decimal.add(a, b)
  end


  def do_decimal_subtract(a, b) do
    Decimal.sub(a, b)
  end

  def do_decimal_compare(a, b) do
    Decimal.compare(a, b)
  end

end

Add the following to test/foothold/decimal_test.exs


defmodule Foothold.DecimalTest do

  use ExUnit.Case
  import Foothold.DecimalStuff 
  import Decimal

  test "test do_decimal_add" do
    assert Decimal.add(2,3) == do_decimal_add( 2, 3 )
  end

  test "test do_decimal_compare_lt" do
    assert :lt == do_decimal_compare(1, 2)
  end

  test "test do_decimal_compare_gt" do
    assert :gt == do_decimal_compare( 2, 1 )
  end

  test "test do_decimal_subtract" do
    # assert 3 == do_decimal_subtract( 5, 2 )
    # assert Decimal.subtract( 5, 2 ) == do_decimal_subtract( 5, 2 )
    assert Decimal.new( 3 ) == do_decimal_subtract( 5, 2 )
  end

  #  def do_decimal_subtract(a, b) do
  #   def do_decimal_compare(a, b) do
end

Now run the tests again:


ericm@latitude:~/elixir.projects/foothold$ mix test --trace
==> decimal
Compiling 4 files (.ex)
Generated decimal app
==> foothold
Compiling 1 file (.ex)
Generated foothold app
warning: unused import Decimal
  test/foothold/decimal_test.exs:5

warning: unused import ExUnit.CaptureIO
  test/more_strings/reverse_test.exs:4

warning: unused import ExUnit.CaptureIO
  test/more_strings/duplicate_test.exs:3


FootholdTest [test/foothold_test.exs]
  * doctest Foothold.hello/0 (1) (0.00ms) [L#3]
  * test greets the world (0.00ms) [L#5]

MoreStrings.DuplicateTest [test/more_strings/duplicate_test.exs]
  * test try duplicate_string [L#7]In the test for make_three_copies
  * test try duplicate_string (0.00ms) [L#7]
  * test try make_three_copies (0.1ms) [L#12]
In the test try reverse

MoreStrings.ReverseTest [test/more_strings/reverse_test.exs]
In MoreStrings.actually_reverse_string with arg ahello
  * test try reverse [L#9]olleha
In MoreStrings.actually_reverse_string with arg hello
olleh
  * test try reverse (0.1ms) [L#9]
  * test ttttttt [L#16]In test tttttt
  * test ttttttt (0.02ms) [L#16]

Foothold.DecimalTest [test/foothold/decimal_test.exs]
  * test test do_decimal_compare_gt (0.01ms) [L#15]
  * test test do_decimal_subtract (0.01ms) [L#19]
  * test test do_decimal_add (0.01ms) [L#7]
  * test test do_decimal_compare_lt (0.00ms) [L#11]


Finished in 0.04 seconds (0.00s async, 0.04s sync)
1 doctest, 9 tests, 0 failures

Randomized with seed 333086

Next add a module to be the main module for a command line app. Put this in lib/foothold/cli.ex:


defmodule Foothold.CLI do

  import MoreStrings.Reverse
  import MoreStrings.Duplicate

  @default_count 4
  @moduledoc """
  Handle the command line parsing and the dispatch to
  the various functions 
  """
  def main(argv) do
    IO.puts "in main for Foothold"

    reverse_stuff()
    # why doesn't it like this?
    actually_reverse_string( "this is my string" )
    revv( "this is my string for revv" )
    IO.puts duplicate_string "this is a string to be duplicated"
    IO.puts make_three_copies "one copy "


    argv
    |> parse_args
    |> process
    IO.puts "Done with CLI"
  end

  @doc """
  'argv' can be -h or --help, which returns :help

  Otherwise it is a github user name, project name, and (optionally)
  the number of entries to format.

  Return a tuple '{ user, project, count }', or ':help' if help was given.
  """
  def parse_args(argv) do
    OptionParser.parse(argv, switches: [ help: :boolean],
                             aliases:  [ h:    :help   ])
    |> elem(1)
    |> args_to_internal_representation()
  end

  def args_to_internal_representation([user, project, count]) do
    { user, project, String.to_integer(count) }
  end

  def args_to_internal_representation([user, project]) do
    { user, project, @default_count }
  end

  def args_to_internal_representation(_) do # bad arg or --help
    :help
  end

  def process(:help) do
    IO.puts """
    usage:  issues <user> <project> [ count | #{@default_count} ]
    """
    System.halt(0)
  end

  def process({_user, _project, _count}) do
    IO.puts "In process"
  end

end

Next, put the following in the mix.exs file for the project:


defp escript_config do
  [
    main_module: Foothold.CLI
  ]
end

Escript is an Elixir utility that turns compiled projects into zip archives.

Then we can compile our application with “mix compile” and run it with “mix run -e ‘Foothold.CLI.main([“-h”])'”.


ericm@latitude:~/elixir.projects/foothold$ mix compile
warning: function escript_config/0 is unused
  mix.exs:30

Compiling 2 files (.ex)
Generated foothold app
ericm@latitude:~/elixir.projects/foothold$ mix run -e 'Foothold.CLI.main(["-h"])'
warning: function escript_config/0 is unused
  mix.exs:30

in main for Foothold
In MoreStrings.Reverse
In MoreStrings.actually_reverse_string with arg this is my string
gnirts ym si siht
In MoreStrings.Reverse.revv with arg this is my string for revv
vver rof gnirts ym si siht
this is a string to be duplicatedthis is a string to be duplicated
one copy one copy one copy 
usage:  issues <user> <project> [ count | 4 ]

That is the basics to get a project up and running as you learn Elixir. As I stated before, I do not like having code floating in space, or making tiny edits to small files.

I think that deploying an Elixir app to production would take more steps, and you have to know more about the Erlang VM, but that should be enough to get you started.

You’re welcome.

Image from Jruchi II Gospel, a 12-century manuscript housed at the National Research Center of Georgian Art History and Monument Protection, assumed allowed under public domain.

Adding Dependencies To The Go Project

In a previous post I wrote that if a language has a standard way of making projects, then that should be taught right away, along with automated tests. Then I wrote a post in which I went over making a simple project with Go (aka “Golang”).

I realized I skipped something important: adding external dependencies. I will re-use the project from the previous post. You should complete the steps in that post before starting this one.

The project already has two packages. This time I will add two files in one of our existing packages. Each file will use a different module. I will also include a test for each.

I picked a couple of project off of the Awesome Go github page.

The first one is stringy (link to module on pkg.go.dev here).


cd spearhead
emacs morestrings/use_stringy.go

Here is the contents of the file:


package morestrings

import (
    "fmt"
    "github.com/gobeam/stringy"
) 

func UseReplaceLast(root_string, old_s, new_s string ) string {
    fmt.Println("in morestrings/UseReplaceLast")
    replaceLast := stringy.New(root_string)
    return replaceLast.ReplaceLast(old_s, new_s)
}

I am only using the “ReplaceLast” function. Notice the line importing “github.com/gobeam/stringy”.

Next I run “go build”:


go build
morestrings/use_stringy.go:5:2: no required module provides package github.com/gobeam/stringy; to add it:
    go get github.com/gobeam/stringy

I will add code to spearhead.go to invoke my function:


fmt.Println("about to call morestrings.UseReplaceLast('this is my string, working with strings, i love strings', 'string', 'rope') : ", 
        morestrings.UseReplaceLast("this is my string, working with strings, i love strings", "string", "rope"))

Before I run “go get”, here is what is in the go.mod file:


more go.mod 
module info/shelfunit/spearhead

go 1.18

Now I run “go get”:


go get github.com/gobeam/stringy
go: added github.com/gobeam/stringy v0.0.5

Here is go.mod:


module info/shelfunit/spearhead

go 1.18

require github.com/gobeam/stringy v0.0.5 // indirect

There are no issues running “go build” and running the executable:


go build
ericm@latitude:~/github/goapps/spearhead$ ./spearhead 
Hello, world.
about to call numberstuff.AddOneToSum( 5, 6):  12
about to call numberstuff.SubtractOneFromSum( 5, 6 ):  10
about to call numberstuff.AddOneToProduct( 5, 6):  31
about to call numberstuff.SubtractOneFromProduct( 5, 6 ):  29
about to call morestrings.ReturnStringTwice("twice"):  twicetwice
in morestrings/UseReplaceLast
about to call morestrings.UseReplaceLast('this is my string, working with strings, i love strings', 'string', 'rope') :  this is my string, working with strings, i love ropes

The next step is to make a test file: morestrings/use_stringy_test.go


package morestrings

import (
    "fmt"
    "testing"
)

func TestUseReplaceLast(t *testing.T) {
    fmt.Println( "Starting TestUseReplaceLast" )
    cases := []struct {
        root, old, new, want string
    }{
        {"Hello, world, i love the world, dog meets world", "world", "food", "Hello, world, i love the world, dog meets food"},
        {"learning go, will go places, gonna be big",       "go",    "wa",   "learning go, will go places, wanna be big"},
        {"I love life, go live your life, go create life", "life",   "lice", "I love life, go live your life, go create lice"},
    }
    for _, c := range cases {
        fmt.Println( "c.root: ", c.root, ", c.old: ", c.old, ", c.new: ", c.new, ", c.want: ", c.want )
        got := UseReplaceLast(c.root, c.old, c.new)
        if got != c.want {
            t.Errorf("UseReplaceLast(%q, %q, %q) == %q, want %q", c.root, c.old, c.new, got, c.want)
        }
    }
    fmt.Println( "Ending TestUseReplaceLast" ) 
}

When I run “go test -v ./morestrings/” we get all the tests for this module. I will only share the output of the test I just added:


=== RUN   TestUseReplaceLast
Starting TestUseReplaceLast
c.root:  Hello, world, i love the world, dog meets world , c.old:  world , c.new:  food , c.want:  Hello, world, i love the world, dog meets food
in morestrings/UseReplaceLast
c.root:  learning go, will go places, gonna be big , c.old:  go , c.new:  wa , c.want:  learning go, will go places, wanna be big
in morestrings/UseReplaceLast
c.root:  I love life, go live your life, go create life , c.old:  life , c.new:  lice , c.want:  I love life, go live your life, go create lice
in morestrings/UseReplaceLast
Ending TestUseReplaceLast
--- PASS: TestUseReplaceLast (0.00s)

If I run “go test -v ./morestrings/use_stringy_test.go”, I get an error:


# command-line-arguments [command-line-arguments.test]
morestrings/use_stringy_test.go:19:16: undefined: UseReplaceLast
FAIL	command-line-arguments [build failed]
FAIL

I am sure some Golang experts are shaking their heads because I did something wrong. As far as I know, while the “go test” command can run tests using a directory as an argument, it does not work as well when you specify individual files.

To test only the test I just created, I can run this command:


go test -v ./morestrings -run TestUseReplaceLast 

I suppose if you wanted to run multiple tests in a specific file, you would need to give them all a name that is common to the tests in that file, but also unique to that group of tests. So you need to be careful when naming your tests. The argument after the “run” is a regular expression. If I wanted to run the tests from the last post in ./numberstuff/multiplication_enhancements_test.go, I would use this command:


go test -v ./numberstuff/ -run Test.*Product

To run the tests in numberstuff/addition_enhancements_test.go, I would use this command:


go test -v ./numberstuff/ -run Test.*Sum.*

The next package I will use is strutil (link to module on pkg.go.dev here). The file using this module is morestrings/use_strutil.go:


package morestrings

import (
    "fmt"
    "github.com/ozgio/strutil"
) 

func UseCountWords(root_string string ) int {
    fmt.Println("in morestrings/UseCountWords")
    return strutil.CountWords(root_string)
}

Again: note there is an import statement for our module. Here is the output of “go build”:


go build
morestrings/use_strutil.go:5:2: no required module provides package github.com/ozgio/strutil; to add it:
    go get github.com/ozgio/strutil

So I run the “go get” command:


go get github.com/ozgio/strutil
go: added github.com/ozgio/strutil v0.4.0

Now we have two modules in a block in go.mod:


more go.mod 
module info/shelfunit/spearhead

go 1.18

require (
    github.com/gobeam/stringy v0.0.5 // indirect
    github.com/ozgio/strutil v0.4.0 // indirect
)

I will add a couple of calls to our new function in spearhead.go:


fmt.Println("about to call morestrings.UseCountWords('this is my string with words'): ", morestrings.UseCountWords("this is my string with words"))
    root_string := "I am again called upon by the voice of my country to execute the functions of its Chief Magistrate"
    fmt.Println("about to call UseCountWords('I am again called upon by the voice of my country to execute the functions of its Chief Magistrate'): ",
        morestrings.UseCountWords(root_string))

And again I call “go build” and run the executable:


go build
ericm@latitude:~/github/goapps/spearhead$ ./spearhead 
Hello, world.
about to call numberstuff.AddOneToSum( 5, 6):  12
about to call numberstuff.SubtractOneFromSum( 5, 6 ):  10
about to call numberstuff.AddOneToProduct( 5, 6):  31
about to call numberstuff.SubtractOneFromProduct( 5, 6 ):  29
about to call morestrings.ReturnStringTwice("twice"):  twicetwice
in morestrings/UseReplaceLast
about to call morestrings.UseReplaceLast('this is my string, working with strings, i love strings', 'string', 'rope') :  this is my string, working with strings, i love ropes
in morestrings/UseCountWords
about to call morestrings.UseCountWords('this is my string with words'):  6
in morestrings/UseCountWords
about to call UseCountWords('I am again called upon by the voice of my country to execute the functions of its Chief Magistrate'):  19

I will make a test file in morestrings/use_strutil_test.go:


package morestrings

import (
    "fmt"
    "testing"
)

func TestUseCountWords(t *testing.T) {
    fmt.Println( "Starting TestUseCountWords" )
    cases := []struct {
        in_string string
        want int
    }{
        {"Among the vicissitudes incident to life no event could have filled me with greater anxieties than that of which the notification was transmitted by your order, and received on the 14th day of the present month", 36},
        {"I am again called upon by the voice of my country to execute the functions of its Chief Magistrate", 19},
        {"When it was first perceived, in early times, that no middle course for America remained between unlimited submission to a foreign legislature and a total independence of its claims, men of reflection were less apprehensive of danger from the formidable power of fleets and armies they must determine to resist than from those contests and dissensions which would certainly arise concerning the forms of government to be instituted over the whole and over the parts of this extensive country", 79},
    }
    for _, c := range cases {
        fmt.Println( "----- c.in_string: ", c.in_string, ", c.want: ", c.want) // , ", here it is: ", UseCountWords(c.in_string))
        got := UseCountWords(c.in_string)
        if got != c.want {
            t.Errorf("ERROR: UseCountWords(%q) == %d, want %d", c.in_string, got, c.want)
        }
    }
    fmt.Println( "Ending TestUseCountWords" )
}

Here is the result of go test -v ./morestrings -run TestUseCountWords:


=== RUN   TestUseCountWords
Starting TestUseCountWords
----- c.in_string:  Among the vicissitudes incident to life no event could have filled me with greater anxieties than that of which the notification was transmitted by your order, and received on the 14th day of the present month , c.want:  36
in morestrings/UseCountWords
----- c.in_string:  I am again called upon by the voice of my country to execute the functions of its Chief Magistrate , c.want:  19
in morestrings/UseCountWords
----- c.in_string:  When it was first perceived, in early times, that no middle course for America remained between unlimited submission to a foreign legislature and a total independence of its claims, men of reflection were less apprehensive of danger from the formidable power of fleets and armies they must determine to resist than from those contests and dissensions which would certainly arise concerning the forms of government to be instituted over the whole and over the parts of this extensive country , c.want:  79
in morestrings/UseCountWords
Ending TestUseCountWords
--- PASS: TestUseCountWords (0.00s)
PASS
ok  	info/shelfunit/spearhead/morestrings	0.002s

In case you are wondering, while reading through post, what those quotes are, it is my duty to inform you, as any good upstanding citizen would, that they are from the first three inaugural addresses of the heads of government of our great country, whose writing style, which leaves much to be desired by people living in the post-Hemingway era, given that inserting entire thoughts in the middle of sentences can introduce unneeded complexity, I have done my utmost to emulate.

Given the length and content of Washington’s second inaugural address, I get the impression he did not want a second term.

You’re welcome.

Image from Synaxarium of Euthymius of Athos, an 11-century Georgian manuscript, image from Wikimedia, assumed allowed under public domain.

Making a Go Project

In a post I mentioned that when learning a new programming language we should start out making projects and tests for our code.

One language I am interested in is Go (aka Golang). Here I will list the steps to make a Go project with some tests. I will also include the source code for each file. I know this is redundant since there is a tutorial for this on the Go language site, but I wanted to make one with multiple packages. [Note 1]

First cd to a directory where you will create your project.


go.dir$ mkdir spearhead
go.dir$ cd spearhead
go.dir/spearhead$ go mod init info/shelfunit/spearhead
go: creating new go.mod: module info/shelfunit/spearhead
go.dir/spearhead$ more go.mod 
module info/shelfunit/spearhead

go 1.18

NOTE: You must use slashes, not dots. I put in dots the first time, and it did not work.

First I will make a few functions that do some simple things with numbers. I will make a couple that add two numbers, and then add one to the total. I will make another function that subtracts one from the total. I will do the same for multiplication.


mkdir numberstuff
emacs -nw numberstuff/addition_enhancements.go

Here is the contents of the file:


package numberstuff 

func AddOneToSum( x, y int ) int {
    return x + y + 1
}

func SubtractOneFromSum( x, y int ) int {
    return x + y - 1
}

Now I will write a few tests for this. This will go in numberstuff/addition_enhancements_test.go. Since I am using Emacs, I will create the new buffer while in numberstuff/addition_enhancements.go. I will probably not have too many Emacs commands going forward. Also: I am not too familiar with the Go toolchain or the Emacs Go mode, so I will be running some tests and commands on the command line. When my Emacs-fu is more powerful, I will be able to do it all in Emacs.


package numberstuff

import (
    "fmt"
    "testing"
)

func Test_AddOneToSum( t *testing.T ) {
    fmt.Println("Testing AddOneToSum")
    result := AddOneToSum(3, 4)
    if result != 8 {
        t.Error("Incorrect result, expected 8, got ", result)
    }
}

func Test_AddOneToSumCases(t *testing.T) {
    fmt.Println("Testing AddOneToSumCases")
    cases := []struct {
        a, b, result int
    }{
        {3, 4, 8},
        {4, 5, 10},
        {5, 6, 12},
    }
    for _, c := range cases {
        got := AddOneToSum(c.a, c.b)
        if (got != c.result) {
            t.Errorf( "incorrect result: AddOneToCases(%x, %x) gave %x, wanted %x", c.a, c.b, got, c.result )
        }
    }
}

func Test_SubtractOneFromSum( t *testing.T ) {
    result := SubtractOneFromSum(3, 4)
    if result != 6 {
        t.Error("Incorrect result, expected 6, got ", result)
    }
}

Now I will do the same for multiplication. Here is numberstuff/multiplication_enhancements.go:


package numberstuff 

func AddOneToProduct( x, y int ) int {
    return (x * y) + 1
}

func SubtractOneFromProduct( x, y int ) int {
    return (x * y) - 1
}

Here is numberstuff/multiplication_enhancements_test.go:


package numberstuff

import (
    "fmt"
    "testing"
)

func Test_AddOneToProduct( t *testing.T ) {
    fmt.Println("Testing AddOneToProduct")
    result := AddOneToProduct(3, 4)
    if result != 13 {
        t.Error("Incorrect result, expected 13, got ", result)
    }
}

func Test_SubtractOneFromProduct( t *testing.T ) {
    result := SubtractOneFromProduct(3, 4)
    if result != 11 {
        t.Error("Incorrect result, expected 11, got ", result)
    }
}

Next I will invoke these from my main file, spearhead.go:


package main

import (
    "fmt"
    "info/shelfunit/spearhead/numberstuff"
) 

func main() {
  fmt.Println("Hello, world.")
    fmt.Println("about to call numberstuff.AddOneToSum( 5, 6): ", numberstuff.AddOneToSum( 5, 6 ) )
    fmt.Println("about to call numberstuff.SubtractOneFromSum( 5, 6 ): ", numberstuff.SubtractOneFromSum( 5, 6 ) )
    fmt.Println("about to call numberstuff.AddOneToProduct( 5, 6): ", numberstuff.AddOneToProduct( 5, 6 ) )
    fmt.Println("about to call numberstuff.SubtractOneFromProduct( 5, 6 ): ", numberstuff.SubtractOneFromProduct( 5, 6 ) )
}

Now run this from the command line.


go build

This will create an executable called “spearhead” next to “spearhead.go”. There are two ways I can run this program:


go run spearhead.go

Or just


./spearhead

Either way gives the following:


Hello, world.
about to call numberstuff.AddOneToSum( 5, 6):  12
about to call numberstuff.SubtractOneFromSum( 5, 6 ):  10
about to call numberstuff.AddOneToProduct( 5, 6):  31
about to call numberstuff.SubtractOneFromProduct( 5, 6 ):  29

To run the test, I need to go into the numberstuff directory:


cd numbertest
go test

Here is the result:


Testing AddOneToSum
Testing AddOneToSumCases
Testing AddOneToProduct
PASS
ok  	info/shelfunit/spearhead/numberstuff	0.003s

I put in some calls to fmt to print out stuff to the command line so I know it is working. For that, we have “go test -v”:


go test -v
=== RUN   Test_AddOneToSum
Testing AddOneToSum
--- PASS: Test_AddOneToSum (0.00s)
=== RUN   Test_AddOneToSumCases
Testing AddOneToSumCases
--- PASS: Test_AddOneToSumCases (0.00s)
=== RUN   Test_SubtractOneFromSum
--- PASS: Test_SubtractOneFromSum (0.00s)
=== RUN   Test_AddOneToProduct
Testing AddOneToProduct
--- PASS: Test_AddOneToProduct (0.00s)
=== RUN   Test_SubtractOneFromProduct
--- PASS: Test_SubtractOneFromProduct (0.00s)
PASS
ok  	info/shelfunit/spearhead/numberstuff	0.003s

If I want to run the test from the root of the project, run go test with the relative path to the project, with or without “-v” as desired:


go test ./numberstuff
ok  	info/shelfunit/spearhead/numberstuff	0.002s

Just to make sure I am not going crazy, let’s change one of the result assertions in numberstuff/addition_enhancements_test.go from 8 to 9:


func Test_AddOneToSum( t *testing.T ) {
    fmt.Println("Testing AddOneToSum")
    result := AddOneToSum(3, 4)
    if result != 9 {
        t.Error("Incorrect result, expected 9, got ", result)
    }
}

That gives us this:


go test  ./numberstuff
Testing AddOneToSum
--- FAIL: Test_AddOneToSum (0.00s)
    addition_enhancements_test.go:12: Incorrect result, expected 9, got  8
Testing AddOneToSumCases
Testing AddOneToProduct
FAIL
FAIL	info/shelfunit/spearhead/numberstuff	0.003s
FAIL

Change it back before moving forward.

To prove that I know what I am doing, I will add another package: morestrings. This is the same package that is used in How To Write Go, but I am going to make a different function.

I will make a file morestrings/duplicate_string.go:


package morestrings

func ReturnStringTwice(s string) string {
    return s + s
}

Next I will make a test file morestrings/duplicate_string_test.go:


package morestrings

import (
    "fmt"
    "testing"
)

func TestDuplicateString(t *testing.T) {
    fmt.Println( "Starting TestDuplicateString" )
    cases := []struct {
        in, want string
    }{
        {"Hello, world", "Hello, worldHello, world"},
        {"eating nun arrays funny", "eating nun arrays funnyeating nun arrays funny"},
        {"negative houses gauge freedom", "negative houses gauge freedomnegative houses gauge freedom"},
    }
    for _, c := range cases {
        fmt.Println( "c.in: ", c.in, ", c.want: ", c.want )
        got := ReturnStringTwice(c.in)
        if got != c.want {
            t.Errorf("ReturnStringTwice(%q) == %q, want %q", c.in, got, c.want)
        }
    }
}

Now we run the tests:


go test -v ./morestrings/ 
=== RUN   TestDuplicateString
Starting TestDuplicateString
c.in:  Hello, world , c.want:  Hello, worldHello, world
c.in:  eating nun arrays funny , c.want:  eating nun arrays funnyeating nun arrays funny
c.in:  negative houses gauge freedom , c.want:  negative houses gauge freedomnegative houses gauge freedom
--- PASS: TestDuplicateString (0.00s)
PASS
ok  	info/shelfunit/spearhead/morestrings	(cached)

Now we update spearhead.go:


package main

import (
    "fmt"
    "info/shelfunit/spearhead/morestrings"
    "info/shelfunit/spearhead/numberstuff"
) 

func main() {
    fmt.Println("Hello, world.")
    fmt.Println("about to call numberstuff.AddOneToSum( 5, 6): ", numberstuff.AddOneToSum( 5, 6 ) )
    fmt.Println("about to call numberstuff.SubtractOneFromSum( 5, 6 ): ", numberstuff.SubtractOneFromSum( 5, 6 ) )
    fmt.Println("about to call numberstuff.AddOneToProduct( 5, 6): ", numberstuff.AddOneToProduct( 5, 6 ) )
    fmt.Println("about to call numberstuff.SubtractOneFromProduct( 5, 6 ): ", numberstuff.SubtractOneFromProduct( 5, 6 ) )
    fmt.Println("about to call morestrings.ReturnStringTwice(\"twice\"): ", morestrings.ReturnStringTwice( "twice" ) )
}

We can run all the tests with this command:


go test -v ./...

That is a period, a slash, and three periods. Sometimes the generator and browser make the three periods into one character.

I know we should be open to new ideas and new ways of doing things, but I wish “go test” just ran all the tests in the project tree. Just like Maven and Gradle. And Leiningen. And Clojure CLI tools And Ruby. And Elixir. Before anyone tries to play the “because Ken Effing Thompson sez so” card, I already do that with the ternary operator: he is the guy who put it in C, and he kept it out of Go.

You’re welcome.

Note 1: I have also been trying to do this with Elixir. For some reason, I have been able to make a few different modules, but I cannot get the tests to run. I also do not get any errors for the tests. I will step through the relevant chapter in Dave Thomas’ book and post about Elixir in the near future.

Image from Évangéliaire de Saint-Mihiel, aka Irmengard Codex, a 10th-century manuscript held at the Colleges of the Université Catholique de Lille; image from Wikimedia, assumed allowed under public domain. “Irmengard, it’s a cerdex.”