2023-01 Austin Emacs Meetup

There was another meeting two weeks ago of EmacsATX, the Austin Emacs Meetup group. For the this month we had no predetermined topic.

#1 was one of the organizers, and is active in the Austin Clojure group. He shared his screen for most of the presentation.
#2 was our professor in OKC.
#3 was a local Javascript/Clojure dev in Austin. Norman
#4 was a local dev in Austin. I think he was #5 back in June.
#5 was one of the organizers, and formerly worked for the City of Austin.

#2 previously presented to the group back in May on developing Emacs Lisp as well as using it for molecular biology. He referenced the presentation he gave for EmacsConf 2021. He also gave a talk at the recent EmacsConf in December 2022. He spoke a bit about how the conference was handled: people pre-recorded their talks, their talks were shown at the scheduled time, and then there was a chat to ask questions. He said some of the talks were very polished. RMS got to do his live. He can ramble live, or he can ramble on tape. What is the difference?

#2 was surprised at how many young people were involved with the conference.

There is an online book club going over Mastering Emacs that #2 attends. You can find out about it here. There are quite a few regular Emacs meetings listed on Sacha Chua’s Emacs new feed, although some are at a bad time for US people. A couple of people in the ATC meeting wondered how Sacha Chua makes the digest; it would be hard to automate putting posts into categories. Does she do it by hand? She has been doing this since 2015. Maybe she gave a talk about it at an EmacsConf one year; I will have to check. I still have not gotten around to watching the talks from previous years. One of the effects of Org mode is that I am not only better at tracking things on my to-do list, I realized I have a very long to-do list.

There was some discussion about GPT-3 packages, which became discussions about GPT-3. #1 gave it some interview questions for Javascript, and he thought it could pass an interview. #2 pointed out that for all the hand-wringing about GPT-3, you still have to know enough to judge the answer.

#1 said he had asked it to write a few stories, and there seems to be a pattern: a group struggles, they persevere, it works out, happy ending. Like every sports movie.

#1 asked me about Rainer Koenig’s Org-mode course. I still have to finish. I got it two years ago, and I learned enough to get stuff done with it, and haven’t gotten around to finishing. I know there is a stereotype of Emacs users who spend so much time configuring Emacs they never get much done. After I got halfway through Rainer’s course, my config has been pretty stable. Rainer is big on key chords (as are most Emacs people), but now when I learn a new mode I prefer to use M-x $FUNCTION_NAME to get stuff done. I have to pause a lot to get the function names for the new key chords he was introducing. I do not know any of the key chords for Org at all, and I see nothing wrong with that. The only criticism I have of his course is that a lot of the dates in his Org files are from a few years ago, so when you go into agenda views, all the items are from a few years ago. If the view is for a week, you will have to press the “b” key to go back dozens of times. The page on Udemy says the course has not been updated since December 2020. But other than that one point, I recommend the course and I learned a lot from it.

#1 went to Udemy to find Emacs courses, and there were more courses on Udemy than we expected.

#5 said he was getting more into macros. Someone pointed out that using the function keys makes it easier. I guess Emacs did not always use F3 and F4 to start and stop macro definitions. There was some wailing and gnashing of teeth about function keys. #1 put prompt into ChatGPT: write a rant about function keys in style of Leviticus. He read it aloud, and I said he sounded like someone who did not know the answer to a question and was just stalling for time. #5 said that is most of the Bible.

Someone said that ChatGPT was just as vacuous as ELIZA. I said, “Why do you think ChatGPT is as vacuous as ELIZA?” He took the question seriously.

#1 looked at a project on Codeberg, which is the what I am thinking about migrating to. A few people talked about getting off Github. #3 noticed people use “github” the way they use google. People do not search for something, but they google it. Since he was upset, we offered to FedEx him some Kleenex.

Then we were back to ChatGPT. There was some discussion about the legal aspects of it. Github Copilot incorporates a lot of open source material from projects with various licenses. Using it could get a company in some legal trouble. Some of the visual AI programs like DALL-E have some distortions that are speculated to be washed out artist signatures.

#1 asked ChatGPT for some stories to prove they are formulaic. He predicted if you ask it for a story about two people falling in love, they always wind up together. He did that a few times, and that is what happened. I suggested a story about Charles Bukowski finding love on Github (the same way people ask DALL-E to make a picture of an astronaut in the style of French Impressionists), but it would not do it. Although it was more polite than Bukowsky. #1 asked for one where the couple finds love on Github and breaks up. ChatGPT did it, but then they got back together again.

#1 asked ChatGPT for topics for the Emacs meetup. Then he asked for the top Emacs plugins. git-timemachine looked interesting to a few people. Searching for this package led me to the Emacsmirror Github repo and website. Then #1 prompted it for presentations about Magit and beacon mode. It gave a pretty good start. No longer will presenters be hurting for presentations.

Then #1 asked ChatGPT for a story about me getting a job using Emacs. I do not remember if he was making stories for everyone, or if this was inspired by something I said; I have stated a few times I would like a job where I could be in Emacs all day. For some reason it had me learning Python (which I know nothing about) and getting a job with that, and then I fall in love with a woman named Sarah in Antarctica. I think #1 just put in my first name and not my full name, but it seemed odd to everyone that ChatGPT inserted Python and going to Antarctica. I did not think Python is the most popular language amongst Emacs programmers; I would have guessed Clojure has more Emacs users.

At the end of the meeting, #5 mentioned he worked for a couple of summers for BP up in Prudhoe Bay. He said some of them were certified engineers who had gotten burnt out with engineering. #1 said he had a college roommate who did that, made a lot of money, and used that high number to negotiate a higher salary. Then #1 talked about neighbors shooting, and speculated they must have spent a lot of money on ammo.

Kleenex, Leviticus, art, literature, romance, oil, and guns: Emacs people can talk about more than just Emacs.

Or, as some people call it, “E-Max”.

I give people numbers since I do not know if they want their names in this write-up. Think of it as the stoner’s version of the Chatham House Rule. I figured that numbers are a little clearer than “someone said this, and someone else said that, and a third person said something else”. Plus it gives participants some deniability. People’s numbers are based on the order they are listed on the call screen, and the same person may be referred to by different numbers in different months.

I am not the official spokesperson for the group. I just got into the habit of summarizing the meetings every month, and adding my own opinions about things. The participants may remember things differently, and may disagree with opinions expressed in this post. Nothing should be construed as views held by anyone’s employers past, present or future. That said, if you like something in this post, I will take credit; if you don’t, blame somebody else.

Image from Codex aureus pultoviensis, an 11th-century manuscript housed at the National Museum in Krakow; image from National Museum in Krakow, assumed allowed under public domain.

 

I Got an Account On Mastodon

I got an account on Mastodon. I am at https://emacs.ch/@EMacAdie. It is on a server for Emacs enthusiasts. I will probably post about more than just Emacs.

I created the account in the browser, but afterwards I posted using the Mastodon client hosted on Codeberg; here is a link to the post. After you install and add a couple of vars to your Emacs config, you type “M-x mastodon“, and it puts a URL to the authorization page in the OS buffer. You go there, then you get the token, or the secret, or whatever OAuth calls it, and you then do back to Emacs and C-y, and you are in. It will put a file called “mastodon.plstore” with all the login info in your Emacs config directory. If you put your config into a git repo, add “mastodon.plstore” to your .gitignore file (or whatever your equivalent is if you do not use git).

One thing the author of the client does not mention is that you cannot authorize the client if you run with “–no-window-system“. The URL I kept getting when I ran Emacs in terminal mode was just the regular page for my profile. I could get to a page listed what apps were authorized, but I could not actually authorize anything. I re-started Emacs without “–no-window-system” and it worked fine. I looked at some of Mastering Emacs, and apparently there are some things that do not work when you are in terminal mode. There are some times I thought I was doing something wrong and could not get something to work. Perhaps the real issue was that I was in terminal mode.

The client repo does mention that if you want to post with a second account, you need to edit your config and restart.

I mentioned the Mastodon client in my post about the December 2022 Austin Emacs meetup.

What does one do on Mastodon? Tweet? Mastocate? Is there another word for it? [Note 1]

The code for the client is hosted on Codeberg. I might move my Github repos to Codeberg. I want to have as little Microsoft in my life as I can. If I do, I might give some money to Codeberg to help keep them going. I might also donate to help keep Emacs.ch running. I have given money to Wikipedia. I will make a list of projects/organizations to make small donations to, and track them in Org.

I will try to post a link to this post from the Emacs client, but I reserve the right to fall back to the web if needed.


Note 1, added after publishing: Per the comments, apparently you “toot” on Mastodon.

Image from the Emperor’s Bible, an 11-century manuscript housed in the Uppsala University Library; image from the Alvin portal, 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.

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.

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. 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.

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, a 10th century manuscript held at the Colleges of the Université Catholique de Lille; image from Wikimedia, assumed allowed under public domain.

2022-12 Austin Emacs Meetup

There was another meeting a week ago of EmacsATX, the Austin Emacs Meetup group. For this meeting there was no predetermined topic.

#1 was one of the organizers. He used to live in Austin, but is now somewhere in East Texas.

#2 was a developer near Dallas. He has been to a few meetings in the past.

#3 was one of our developers in OKC (Still the Artist Known as Number Three).

#4 was a developer in San Antonio. I think this was his first time.

#5 was a developer in Seattle, who was originally from Romania.

This meeting was just a few days after EmacsConf 2022. #3 and #4 listened to some of the talks as they happened.

They both listened to Richard Stallman’s talk What I’d like to see in Emacs. They agreed with Stallman that configuring Emacs with Javascript would be a bad idea, although they were against it for more technical reasons. I think using Javascript for anything is a bad idea. Stallman said he wants to be able to edit formatted documents with a WYSIWYG interface. #4 said he asked Stallman about this during the talk, since #4 felt that a WYSIWYG interface would tie information to its style or appearance and guide or possibly limit the users’ thinking, whereas a text-first philosophy allows people to process information they way they want. Both he and #3 did not feel Stallman really answered the question, or had thought it through very well.

The WYSIWYG topic led to lots of talk about what sites can run in Emacs, cookies, JS, and what should Emacs be able to do.

#3 thought it was interesting that Stallman does not use packages.

Someone mentioned that they were shocked Stallman did not know what Magit is. I did some digging, and the only reference I could find to this was a post on the Emacs-development mailing list in 2014. AustinATX did not start having evening meetings until 2016, so I was not as plugged in to Emacs as I am now. I have heard in the past few years that some people have gotten into Emacs because of Magit. I have no idea if that was the case in 2014.

There was some talk about EmacsConf and conferences in general. #3 liked that the talks at EmacsConf were short, like lightning talks, and that left more time for Q&A. #2 said conferences exhaust him. I mentioned that every time I have gone to a conference with two tracks, there will always be a time slot with two talks on topics that interest me, followed by a time slot in which neither topic interests me. A few times I went to conferences and stayed at a less expensive hotel; each time I wound up paying the difference in parking or cab fare.

#3 want to go back and watch EmacsConf presentation on eshell. #2 watches videos by browsing feeds from sites rather than sites themselves with elfeed; for YouTube he uses elfeed-tube and youtube-sub-extractor. I will have to look into these. I use emacs with the –no-window-system switch, so I will have to make a new alias to try this stuff. Anything GUI or pretty does not render when Emacs is run in terminal mode.

At some point I mentioned I bought Mastering Emacs over the holiday weekend. The author ran a sale for 20% off. He runs sales a few times a year. Keep an eye out for a good deal.

#3 said the Crafted Emacs community has moved to Mastodon. He did not give a link, although there is a Mastodon server dedicated to Emacs. There is a Mastodon client in Emacs hosted on Codeberg.

There was talk about ChatGPT. Most of us were worried about our futures.

#2 had a suggestion for next meeting: pool ideas about what we want in Emacs, make a list, maybe make something.

#1 would like a theme that adjusts based on time of day, and someone suggested Circadian. This question came up in the January meeting. I guess they are not reading this site.

#4 wants a starter kit for specific workflow. I cannot remember what it was. This led to a lot of discussion about what starter kits should do, and can non-experts run multiple configs, how easy is it to get started.

#5 asked if anyone else uses Emacs on Windows. I mentioned I use it to keep notes in Org mode. #2 has Linux on a VM on Windows, and provided a link to running Emacs on Windows with WSL2.

#5 has issues with using Emacs in Windows shop: Can he share his tools/scripts with others at his job? Maybe not, but if it makes him more productive than everybody else, who is using the wrong tool? #5 also asked for tips on how to learn Emacs; I think he said this was his fifth attempt, but I also got the impression he is proficient with Emacs, and maybe Emacs Lisp as well. #2 talked a bit about how he learned Emacs, getting through a list in Org mode, starting small and building up. I pointed out nobody looks at JDK javadoc and says, “There are thousands of classes, I could never learn it all.” They learn enough to get something done, then learn more as they need it. But for some reason people seem to freeze learning Emacs. #2 said it can be a long road, but it is worth it. He decided to go cold turkey with IntelliJ and just use Emacs. He uses Emacs because it helps do things he cannot do with other tools. He also said something that could go on a tombstone: “The road to simplicity is never straightforward.” Or he was just quoting Yogi Berra.

You’re welcome.

I give people numbers since I do not know if they want their names in this write-up. Think of it as the stoner’s version of the Chatham House Rule. I figured that numbers are a little clearer than “someone said this, and someone else said that, and a third person said something else”. Plus it gives participants some deniability. People’s numbers are based on the order they are listed on the call screen, and the same person may be referred to by different numbers in different months.

I am not the official spokesperson for the group. I just got into the habit of summarizing the meetings every month, and adding my own opinions about things. That said, if you like something in this post, I will take credit; if you don’t, blame somebody else.

Image from a 12th-century manuscript housed at the Dujcev Center at Sofia University in Bulgaris; image from Wikimedia, assumed allowed under public domain.

Password Managers Really Could Provide Sufficient Security

BLUF: Increased use of password managers would like to improvements in security for both individuals and businesses, and would make a lot of “post-password” security measures unnecessary.

More use of PMs will not solve all security issues. I do not think we can talk about a site or a database (or anything) as being “secure” or “insecure”. I think it is a matter of degrees. We can only talk about “more secure” or “less secure”. That said, I think password managers would go a long way towards solving a lot of problems. Hopefully it is not too late to make improvements. And I know vendors are trying to push things in a different direction.

I have been listening to old episodes of security podcasts: Security Now and Risky Business. I started at the very beginning for both, and I am up to about 2012 for Security Now and 2013 for Risky Business. I got up to 2011 with Security Now. They said passwords are dead, but I think password managers could solve a lot of issues and improve security. I think if people use them more, we would not need to move to a password-less future. I use KeePassXC. I hope that KeePassXC will still be useful in a world where vendors want to control access.

One is issue with passwords is password reuse. A lot of people re-use passwords because they cannot think of and/or remember new passwords. So if hackers get a database of users for one site, they will try those credentials on other sites, and sometimes get they lucky. With a PM, you only have to remember the password or passphrase for the PM. It can create new passwords for you so you do not have to remember them or re-use them. And if a site has a policy about changing passwords on a regular basis, again you can create new ones easily. One of the times Kevin Mitnick was on Risky Business he said he was trying to get information about his credit card. He had questions about his account, and the woman on the phone said he needed to provide his password. She starting giving him hints because he had trouble remembering. If he had a good password manager, he would have the second password available, and the support desk would not be practicing bad security.

With KeePassXC, you can create attributes which are stored as key/value pairs. You can use these for password reset questions. Many famous people have had credentials hacked since their answers are public information: mother’s maiden name, where were you born, where did you meet your spouse, etc. Much of this information can also be obtained for just about anyone if you know where to look. One way around this is to make answers up. Tell a site you were born in a place you have never been to. Put in a random phrase for the title of your favorite movie. But if you make stuff up, how do you remember what answers you gave for each website? Attributes in KeePassXC can take care of that for you.

You can also attach files to an entry. This would be a good way for people who are not very tech-savvy to send each other sensitive files. Make a database file with one entry and attach your files. Put it on Google Drive or DropBox, and email the link to whoever you want to send it to. Then send them the password or passphrase (I think passphrases are better and should be used whenever possible) some other way. If you send the link to the file in an email, do not email the passphrase. The best thing would be to do it over the phone, and they can type make an entry in their own KeePassXC database and type it in there.

I used to be one of those people who thought what if someone gets your PM? Well, we have seen the alternative. If you have a strong passphrase, I think you will be fine. As Bruce Schneier has pointed out, there is nothing wrong with writing your master password on a piece of paper. The important thing is what do you do with that pice of paper.

Could your PM passphrase be brute-forced? I suppose so. But there are counters or potential weaknesses for just about everything. I think if you take a few intelligent steps to be secure you should be fine. “What if they do A? What if they do B? What if they do C?” That cycle never ends. Nothing is perfect.

I use KeePassXC locally, running as a standalone application. I do not hook it into my browser. I clear cookies when I exit my browsers. I think storing passwords in a browser is a bad idea. As with many things in security, doing something that is more convenient is not always a smart idea.

And I hope I never have to use an online PM. With online PMs you are adding more risks to your threat model. What if their site is offline? Then you cannot log in to anything. Suppose your online PM is down, but your bank, your utility and Netflix are all up. Now you have to wait to pay your bills and watch movies until the PM site is back up. These sites have been breached, particularly LastPass (see here and here). According to this page, just about every PM has been hacked in some way. It mentions KeePass, but not KeePassXC, which is a fork (actually a fork of a now-dead fork) and has a separate source tree. Another issue with online PMs is a lot of them have different tiers of service, and many of them (including the popular ones like 1Password and LastPass) have bumped some features up to a higher tier, leaving some users high and dry.

As to whether or not KeePassXC runs on a phone: I do not care. I do not use my phone for too many things except as an alarm clock, a stop watch, two-factor auth when I have to and ignoring the few calls I get. Most apps are just ways to get you to spend more money. I do not need help spending more money. I need help making more money. And I just hate this phenomenon of people being on their phones all the time and trying to do everything on their phone. I have seen people pull half-way out of parking spaces and stop because they were looking at their phone. I don’t know if phones are making people stupid or giving them an excuse to let their freak flag fly and stop hiding their idiocy, but I am not interested in doing everything on my phone. When I see people at the grocery store walking way too slowly looking at their phones, I am always reminded of that line from Blaise Pascal: Mankind’s problems are due to his inability to sit in a quiet room alone.

Every month I make a copy of my KeePassXC files (as well as other files, including my Org-mode files) onto a couple of USB flash drives. Between KeePassXC and Org-mode I am becoming more disciplined with all my information.

One way to be disciplined is consolidation. One argument against PMs is that they put all your eggs in one basket. True, but it is a basket you can control. We put “all our eggs in one basket” all the time. How many people live their lives on their phone? How many people keep all their physical keys on a keychain? As Troy Hunt pointed out, PMs are not perfect, but they are better than the alternative.

Some people might object to KeePassXC because it is open source. One person on Hacker News argued against open source PMs on legal reasons. He tried to explain KeePassXC to his father (who is a lawyer), and his father was horrified there was “no legal entity to hold accountable”. Either that person’s father is a bought-and-paid-for shill, or really really dumb. If you sue someone (even if they are bigger than you), they can turn a course case into an endurance contest. Control what you can control.

Now a lot of vendors are pushing a “passwordless future”. Vendors cause the problem, then they try to solve the problem. I do not want to give vendors any more control over my life. I do not want to be forced to use a phone, especially a specific phone vendors. I know I am kind of old-school in how I access the web. I use a browser on a laptop. If other people want to go through life not saying if the sun is up until Apple tells them it is, they can do their thing and I can do mine. I do not want vendors having any sort of access to my fingerprint, or my face, or be forced to use anything by Microsoft, or buy a stupid iPhone. I cannot change my face, or change my fingerprint, and I do not want to be tied to a device. Everything I read about this “passwordless future” requires me to do things the way they want me to do it, and not the way I want to do it.

It is odd to me that a lot of people in the US are distrustful of the “guv-ment” and complain about taxes, yet they have no problem doing whatever Microsoft tells them without question. And there are a lot of people who pride themselves on not trusting Microsft, yet will do whatever Apple tells them (and constantly buy overpriced products year after year).

You’re welcome.

Image of David and Goliath from a 9th-century manuscript  Vat. gr. 752 from the Vatican Library; image from Wikimedia, assumed allowed under public domain.

I Now Own Only Linux Machines

About nine months ago I got a small desktop computer running Linux from a company in Colorado named System76. I got the Meerkat. I still have to use Windows for work, but after about 21 years of either dual-booting or having one each of Linux and Windows, my machines are now free of Microsoft.

There is a saying that BSD is for people who like Unix, and Linux is for people who hate Windows. Either way works for me. One reason I went with System76 is that I wanted to buy something with Linux pre-installed [Note 1]. I think it is important to send a market signal that there is demand for Linux. Yes, I could buy something with Windows on it and install Linux. But I am tired of installing OSes. And it does not seem as easy as it used to be. I know that Unified Extensible Firmware Interface (UEFI) has replaced BIOS. I think I do not quite get the difference between a BIOS and a UEFI, and I do not care. I do not want to spend my time with low-level stuff. I have read some articles and comments that UEFI makes it harder to install Linux (or at least it did: see articles here and here). I think for a while Microsoft was the only UEFI signing authority.

Some people have suggested that I go for Apple. From one proprietary vendor to another. How much money does MS get for a laptop with Windows pre-installed? Maybe $50 or $100? Giving Apple an extra $1000 to not give MS $100 does not seem smart, especially when Apple is not what I really want anyway.

I also wanted to get away from Ubuntu. So far I have liked it, but I do not like working with snaps (see Hacker News posts here, here, here and here). They use a lot of disk space, sometimes they use up a lot of memory, and they tend to update whenever they want and not what the user wants, sort of like Windows. System76 has an Ubuntu derivative named Pop!OS (page here, subreddit here; and yes, the exclamation point and underscore are part of the name) and it does not use snaps. Another Ubuntu derivative named Linux Mint has also dropped snaps (HN threads here and here). If I had to install, I think I would go with Mint.

Before this I had two laptops: one I got used in 2017 from a local vendor named Discount Electronics that I installed Ubuntu on that I used as my main system, and one that I got in 2014 running Windows 8 that I mostly used to collect podcasts and watch videos. The Windows laptop was getting slower and slower, the battery stopped working a long time ago, and it would update whenever it wanted. When it tried to update, I used to try to tell it to update in 5 or so hours, but then it would bug me again five minutes later. Typical Windows: doing what it wants, and not what I want. At some point, it took so long to start up I thought that it was only a matter of time until it died and I should replace it soon.

I looked around for a few vendors. I asked on a local list about minis [Note 2] and laptops. I mentioned System76 and the Librem Mini by Purism. Someone else mentioned minis by Simply NUC. Other Linux vendors include KFocus by MindShare, Tuxedo Computers, Raptor Computing Systems (their systems run Linux on Power chips and are very expensive) and Starlabs Systems (their systems do not contain dark matter). Lenovo sells system in the US, but a lot of people do not trust them. Although I did not go with Ubuntu, they do have a list of hardware certified to run Ubuntu; it is a good resource to have if you want to install something a distro derived from Ubuntu.

I like the Meerkat. I had to buy a USB port because it did not have enough ports for me (sometimes I hook in a flash drive and headphones while my mouse and keyboard are hooked in). I also had to get an adapter to go into the USB port because the audio jack was not compatiable with my speakers. It seems to add OS updates more frequently than I expected (not necessarily kernel updates, but other packages it labels as part of the OS), and I am not able to make a few small configurations that I can on my Ubuntu machine. For some reason there are two Emacs installations on the system. And it still comes with vi installed. But other than that, I am happy with it and I would recommend it to anyone who wants a Linux system. I like the fact that OS updates might require a restart, but I can put that off as long as I want.

To sum up, I like System76. As ads would say in the days of yore, they are purveyors of fine Linux machines, and make systems that can handle your computing needs.

You’re welcome.


Note 1: I posted a question asking for recommendations for Linux machines. I said I wanted to buy something pre-installed. Someone asked me why. Why not? I am tired of installing OSes. I do not want to get something to have an excuse to install an OS. I have done more times than I wanted to, I never learned anything from it, and I am tired of doing it. I realize that somebody has to know how to do it. That does not mean everybody has to know it.

In the past I have asked people if there are routers with Linux pre-installed, mentioning that while I could install Linux, I have no desire to do so. Almost every time, some idiot says, “Why not install OpenWrt?” Because I am sick of installing OSes. If someone says, “I do not want to do X”, and you suggest, “Why not do X?”, you are not insightful, you are not funny, you are just being a jerk. Or at best obtuse

Note 2: Some jackass replied “Eric, you’ve got to make sure you keep your terms proper and straight. A mini-computer is one step up from a PC, and the next step to a main frame. These are very much like running a very big system.” First off, Dick, we are not friends. I have no idea who you are. Do not refer to me on a mailing list by my first name. Your parents must have seen the future when they decided to name you “Richard”. Secondly, the vendors call them minis. That works for me.

Most of the responses were constructive. Just not that guy.

For a community that talks a lot about freedom, some Linux people have very rigid ideas about how other people are supposed to act.


Image of the women at the tomb from Poussay Gospels, a 10th century manuscript now housed in the Bibliothèque Nationale de France, image from Biblissima Portal, assumed allowed under Public Domain. Telling people that death has been conquered seemed like an appropriate image.

2022-11-20: New Stuff I Learned: Grep and Databases

I recently learned a couple of new tricks, and I want to share a practice I engage in that I have gotten some compliments on.

I have worked on a few Java web applications, and instead of searching with the IDE, I use grep in cygwin. Sometimes the term I search for is not just in a Java file, but it is also in a Javascript file that has no line breaks. (Seriously, why do people make files like this?) This makes the results harder to read. One possibility is to pipe the results of the grep to another grep:

grep -riIn someString * | grep -v '.min.js' | grep -v '.js.map'

The disadvantage of this method is you lose the color in the output. I read the man page, and I found there is an exlusion flag: –exclude. Even better, you can use it more than once:

grep -riIn --exclude=*min.js --exclude=*.js.map someString * 

I also found out the -I option can exclude binary files, which were also polluting the output.

The database we use is Oracle. I started using the Explain Plan to analyze some queries. You can significanly reduce the cost of a query by using upper to compare strings. Going from this:

where some_column like 'some string%'

to this:

where upper( some_column ) like upper ( 'some string%' )

reduced the cost of a few queries from 20,000 to 40,000 to between 30 to 40. I thought that using “upper” would take longer, since it has to run that comparison on the whole table, and that using equals would be faster, but using “upper” is faster. I do not know if this also holds true for other databases. On Oracle “upper” seems to be faster than “lower”.

Another thing I want to mention is that I have gotten a few compliments on how I format insert statements. I line the column names up, and I put in comments in the values() part of the statement for the name of each column.

You have probably seen insert statements like this:

insert into us_state( state_id, state_name, capital,largest_city, abbreviation, population, median_age, persons_sq_mile,nickname, area_sq_mile, avg_elevation, north_latitude_deg, north_latitude_min, south_latitude_deg, south_latitude_min, east_longitude_deg, east_longitude_min, west_longitude_deg, west_longitude_min, created_date, created_by, modified_date, modified_by)
values (func_to_get_next_id(), 'Illinois', 'Springfield', 'Chicago', 'IL', 12882135, 39, 231, 'Prairie State', 57915, 600, 42, 30, 36, 58, 87, 30, 91, 31, sysdate(), user_id, sysdate(), user_id);

I have some that are worse than that. Imagine that statement with no spaces after the commas.

Compare that to this:

insert into us_state( 
    state_id,           state_name,         capital,            largest_city,       abbreviation,       population,         
    median_age,         persons_sq_mile,    nickname,           area_sq_mile,       avg_elevation,      north_latitude_deg, 
    north_latitude_min, south_latitude_deg, south_latitude_min, east_longitude_deg, east_longitude_min, west_longitude_deg, 
    west_longitude_min, created_date,       created_by,         modified_date,      modified_by 
) values (
    func_to_get_next_id(), -- state_id
    'Illinois',            -- state_name
    'Springfield',         -- capital
    'Chicago',             -- largest_city
    'IL',                  -- abbreviation
    12882135,              -- population
    39,                    -- median_age
    231,                   -- persons_sq_mile
    'Prairie State',       -- nickname
    57915,                 -- area_sq_mile
    600,                   -- avg_elevation,
    42,                    -- north_latitude_deg
    30,                    -- north_latitude_min
    36,                    -- south_latitude_deg
    58,                    -- south_latitude_min
    87,                    -- east_longitude_deg
    30,                    -- east_longitude_min
    91,                    -- west_longitude_deg
    31,                    -- west_longitude_min
    sysdate(),             -- created_date
    user_id,               -- created_by
    sysdate(),             -- modified_date
    user_id                -- modified_by
);

It is a little more typing up front, but it is less thinking later on [1].

I also reformat numbers in my notes. I keep notes as I work on stories/defects. If I get an id or a long number from a database, I will copy it, and then add dashes every third number:

4198275630/419-827-563-0
5832064197/583-206-419-7

Years ago I worked on a project where the IDs were a string based on the date down to the nanosecond with no dashes or separators. So I was dealing with very long strings of about 30 characters, half of which I did not need. I got tired of parsing long numbers in my head. More typing means less thinking. The fewer plates I have to spin in my head, the better. Granted, a lot of things that could reduce my coginitve load are usually out of my control, but I change what I can change.

You’re welcome.

Note 1: The formatting is lined up in the WordPress editor; sometimes it looks crooked in the final version. Things like this are why I sometimes think about going with a static site.

Image from Golden Gospels of Henry III, aka Codex Aureus of Speyer or Speyer Gospels (Speyerer Evangeliar), an 11th-centry manuscript housed in the Royal Site of San Lorenzo de El Escorial; image from Wikimedia assumed allowed under public domain.

Learning Languages Test-first and Project-first

Sometimes I wonder why don’t books and tutorials teach programming languages closer to the way web apps are taught and made: By making projects up front, and by incorporating tests right away.

I am going through some Pluralsight tutorials for Go and Elixir, and while there is a lot of information in them, in most of them the code is not well organized. Only one of the dozen or so Go tutorials that I have been through makes a project. Why not do that every time? That is how we make web apps, especially since after Rails most frameworks are created with generators. You can make projects with most languages, but for some reason it is not emphasized. Granted, Go is a language where you can run code outside of a project: You can just put code into “some_file.go”, and then type “go run some_file.go”, but in my opinion it is a bad practice.

Several years ago, I went through a Rails bootcamp. After I was done, another alumni and I started a small study group to go through the RSpec book by David Chelimsky et al. We only had a few meetings, and I do not remember how far in the book we got. We thought that testing was not covered enough in the bootcamp, and we thought it was odd that some languages (like Ruby) have a strong “test-first” culture but when it comes to learning the language tests are treated like an afterthought. If we tell people to write their apps tests-first (or at least simultaneously with their code), why not teach languages test-first? A lot of books wait until halfway through before talking about tests and/or projects, or leave it until the end.

Even Dave Thomas holds off on projects and testing. I thought one of the guys famous for being part of the agile/extreme/TDD movement would start out with tests right away. The pickaxe book and his Elixir book do not get to them until halfway through; it is chapter 13 out of 25 in the Elixir book. Chapter 13 starts with: “Let’s stop hacking and get serious.” Why not start out serious?

It also might help in learning a language. If you are going through a tutorial, I think it is better to have the different sections as files in a project as opposed to just one-off files or snippets floating in space.

It might seem like it is too much for beginners. But what is another chapter in a 200+ page book? And people should know it anyway. If we want people to be competent professionals, they should learn the proper way to do things from the beginning. I think we need to get rid of this idea that a smart developer can learn a language in a weekend. Yes, syntax can be picked up quickly. But to really be good at a language, you need to know the libraries, frameworks and tools. And how to structure your code so that other people can understand and use it.

To introduce people to projects and tests, a tutorial could make a package (or namespace or whatever your language calls them) that just has a few functions/methods that take a couple of numbers, sums them, and adds one to the sum. And another method that subtracts one from the sum. And you could have another package that does the same for multiplication.

I might re-do some of the Pluralsight tutorials by making them into projects. I noticed there is now one for the Gin web framework. Perhaps someone at Pluralsight read my post a few months back on things I do not like about Javascript. They have a tutorial on Go web services that includes a massive Angular app; it takes up more than 500 MB on my hard drive. I wrote it would have been easier (and better for learning Go) to just make a web app in Go.

I was looking for a way to make a project with Go, when I found a video that creates a project while using VS Code. This tutorial follows a page on the Go site. One thing I do not like about Go is that “go test” does not run tests in all the sub-directories by default. Maybe it does and I am just not as familiar with it. But “mvn test” or “gradle test” or “lein test” will run all the tests.

Keep in mind, I am not a TDD purist. I do not always write failing tests first. I have no issue with writing tests after code; Ken Kousen calls it GDD: Guilt Driven Development. I also have no problem with tests hitting the database. A presenter at Austin Ruby years ago pointed out that you can get mocks and stubs to do what you want. If your code has a lot of mocks and stubs, are your tests telling you that your code does what you think it should do, or are they telling you that you are good at mocking and stubbing? Some TDD people say it is fine to use an in-memory database. Why not use an actual database? You are still going outside your code either way.

If you are a TDD purist, do not bother arguing with me. You will not change my mind, and NOTHING ever changes the mind of a purist. Not even the fact that purists are more likely to turn people off to something than turn them on to it. I announced my Groovy Email Server on a list somewhere, and someone got pissy with me because the first thing they did was run tests, and they failed because there was no database connection. Even though the README clearly states that you need to set up a database first. Like Will Rogers said, some people read the sign, and some people just gots to touch the electric fence.

You’re welcome.

Image from León Antiphonary, a 10th-century manuscript hosted at Virtual Library of Bibliographic Heritage, image allowed under Creative Commons CC BY 4.0.

2022-11 Austin Emacs Meetup

There was another meeting a week ago of EmacsATX, the Austin Emacs Meetup group. For this meeting there was a predetermined topic.

The topic was “Mastering Crafted Emacs”. The speaker was one of the developers in OKC (not the professor), aka The Artist Formerly Known As #3. He gave a more formal talk similar to the off-the-cuff one he gave the previous month on Crafted Emacs, particularly a module implementing configuration options covered in the book Mastering Emacs.

Fourteen people were on the call. We did not go around the horn and introduce ourselves. I did look at a few of the Meetup profiles of the respondants. I think we have a person in India, one in Germany, and one in Nigeria. So now all we need is someone from South America and Australia, and our global dominance is complete. A few people dropped off during the presentation; for the international members, it was very early or very late.

This time, he presented the information in a different order. He went over the basic concept of Crafted Emacs (not as complete as Prelude or Doom, but not leaving you high and dry either; you can still do it yourself without having to do it all yourself). He talked about Mastering Emacs, and again recommended that everyone get it. Even an experienced Emacs user can learn something, and you get free updates for every Emacs release as long as he keeps updating it.

The module can be found here on Github. It provides completion, window configuration, buffer switching customization, and turns on the following modes: winner-mode, flyspell-mode, ibuffer, and uses hydra to configure dumb-jump. He made a VM on his machine, and started with a fresh install of Emacs. He then added Crafted Emacs, and enabled different parts of the file and demonstrated what capabilities it gave him.

There was some general discussion after the presentation. Most of the questions were answered by The Artist Formerly Known As #3. Someone mentioned Tree Sitter (site here, Github repo here, Emacs module site here, Emacs module Github repo here). Per its website, Tree-sitter is a parser generator tool and an incremental parsing library. It can build a concrete syntax tree for a source file and efficiently update the syntax tree as the source file is edited. It can be used for syntax highlighting, and it can prevent your syntax highlighting from breaking when you start typing. Most language modes would need to be re-written since they currently use regular expressions. It would allow Emacs do for other languages what Paredit does for the Lisp family: you can hide sections of the code, like loops and if/else branches, instead of just folding methods or functions. Combining it with Language Server Protocol could make Emacs more competitive against IDEs. Quite a few members were excited about it. I thought it was another example of other languages finally being able to do something that Lisp could do for years, which is a good summary of the past 40 years of computer science.

Another member asked about require and use-package. The basic idea is that use-package is a macro that calls require, and throws in some housekeeping like error-handling, as well as installing a package on your system if it is not already there. Unlike a package manager, use-package will not keep packages up to date.

A couple of members also talked about corfu and company, which both handle completion. There is an article comparing them here. One member said that understanding search and search results can make you faster if you are good at them.

You’re welcome.

Don’t forget: EmacsConf 2022 is happening online on 2022-12-03 and 2022-12-04.

I give people numbers since I do not know if they want their names in this write-up. Think of it as the stoner’s version of the Chatham House Rule. I figured that numbers are a little clearer than “someone said this, and someone else said that, and a third person said something else”. Plus it gives participants some deniability. People’s numbers are based on the order they are listed on the call screen, and the same person may be referred to by different numbers in different months.

I am not the official spokesperson for the group. I just got into the habit of summarizing the meetings every month, and adding my own opinions about things. That said, if you like something in this post, I will take credit; if you don’t, in this case you get what you pay for.

Image from MS. Auct. E. 5. 11, a 10th century manuscript housed at the Bodleian Library, University of Oxford; image under Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)