2023-03 Austin Emacs Meetup

There was another meeting a couple of weeks ago of EmacsATX, the Austin Emacs Meetup group. For this month we had no predetermined topic. However, as always, there was mention of new modes, packages, technologies and websites that I had never heard of, and some of this may be of interest to you as well.

#1 was one of the organizers; he used to live in Austin and now lives in East Texas.
#2 was a programming instructor in NYC.
#3 was The Artist Known as Number Three, the Esteemed Gentleman From Oklahoma.
#4 was a devops engineer for a quantum computing company in Madison, WI (he is in Madison, but the company is based elsewhere).
#5 was the other organizer, formerly working for the City of Austin.
#6 was our professor, the Other Esteemed Gentleman From Oklahoma.

When I dialed in the guys were talking about how to keep their kids out of their home offices, although some allow them to go in sometimes. #3 said his kids are adults, and they still do not go into his office. His adult daughter will not cross that yellow line.

#4 talked about the company he works for and his use of Emacs. They make quantum computers by using lasers to trap atoms in a vacuum. I asked him if he worked for Rigetti Computing, but he said it was another company. I guessed Rigetti because 1. It is the only quantum computing company I could name, and 2. Whenever someone on the web asks who uses Common Lisp, a Rigetti person mentions they use Common Lisp. I assume a company that uses Common Lisp would have a lot of Emacs users. He explained that Rigetti makes a chip, while making a quantum computer with trapped ions is a completely different process. #4 said he does not know too much about the lasers and that there is an engineering team that configures them. He said that the configurations are stored in Org files and the process can be run multple times from the same configuration.

Still, I think it is interesting that Emacs can be used to control lasers. And because there were some snarky comments in the chat, he was compelled to point out the lasers are not mounted on sharks.

#4 did know more about lasers than the average person (like what the can and cannot cut through), and he and #3 spent a few minutes discussing lasers. #3 worked with lasers in the Navy. I remarked that #3 seemed like a Renaissance Man: He uses Emacs, he was in the military, he shoots lasers, he has had some music from different genres on in the backround during meetings.

#2 introduced himself. He found out about the group from #4. They met through the nanofiction community. #2 has been using Emacs for 2 years, and came to Emacs from Vim because he thought Vim was too limited. #4 came to Emacs from SciTE.

I mentioned that I wished the developer from Dallas (who was last month’s #2) was on the call. He was speaking to a newcomer who was having a hard time getting into Emacs that many people see Emacs as an editor, tool, or IDE, and while it is those things, ultimately it is a Lisp REPL. My power went out, and I wished I heard him finish the thought. A few people said he went on for several minutes. This led into a rehashing of some of last month’s topics, including Verb mode. #2 thought we said “Bird mode”. It turns out there is a Bird mode; none of us had heard of it or could tell what it does from the sparse Readme.

The two words sound very similar. The word for last month’s mode is “verb”, and connotes action. The word for this month’s mode, on the other hand, has a more avian flare to it. The word for this month’s mode is an ornithogical expression, as it were. What is the work? My friends, “bird” is the word. (According to Wikipedia, it took four people to write that song.)

#4 shared link to RDE, a Scheme repo for reproducible development environments. He and #2 talked for a few moments about Guix, which uses Scheme for configuration.

I asked about an issue I was having with REPLs in CIDER: It would hang when I hit return. The solution was to add this to the Emacs configuration:


(define-key paredit-mode-map (kbd "RET") nil)

This solution was also suggested at Emacs Redux. But when I added it, I kept getting this error:


Symbol's value as variable is void: paredit-mode-map

#4 said it was because it was trying to set paredit-mode-map before paredit was loaded. I am not sure why that would be happening, since I put this at the end of my file. He suggested I change it to this:


(with-eval-after-load 'paredit (define-key paredit-mode-map (kbd "RET") nil))

That worked. I do not know why I was getting the error even though I put the “define-key” towards the end. Perhaps it is time to read Mastering Emacs, the Emacs manual, or perhaps both. Anyway, devops for the win.

I edited my config, and tried out the solution while the conversation continued. When I came back, they were talking about configs and start-up times.

Then talk shifted to startup time. #3 and #6 finally met up in OKC, and #3 helped #6 with his Emacs init, and significantly reduced his startup time. #3 said the basic idea was to use use-package to defer what you need until you need it. Now #6 loads his packages at the beginning using add-to-list to add packages and then configures them with use-package. He has about 155 packages.

Now that I think about it, use-package used to get mentioned a lot at these meetings, but lately it has not been mentioned as much.

#4 runs Emacs server on login, and then runs Emacs client when he needs to edit files, like bluetooth: it starts when you login, not when you connect a device. If #4 comes back on later, I will have to ask him if he keeps any instances going all day, starts and exits, or some combination. He also said that other people on his team use Emacs and talked about their configs, but I did not add that to my notes.

#2 asked about tiling window managers, and #3 talked about WMs that he has used. I use Emacs with the “–no-window-system” option, and just stick with the GUIs that Ubuntu and System76 give me, so some of this went over my head. #3 avoids i3wm because others use it. Not only is he a Renaissance Man, he is a Hipster Renaissance Man.

#3 mentioned he tries to live in Emacs as much as possible, but is not an extremist. He uses Gnus to check email and does a lot of things that most people do in other programs, but he admits there are some things we should do outside of Emacs. He does not play music or browse the web in Emacs. He tried does that. Checks Gnus and email in Emacs. Lives in Emacs. He tried using Emacs to access KeePassXC,but did not like it. I also tried it using keepass-mode, but I did not like the fact that I could not sort the entries in a folder alphabetically. I also tried accessing my database on the command line with keepassxc-cli and I still could not figure out how to list the entries in a group the way I wanted.

#1 talked about using Chemacs, using different profiles. #3 said there is a with-emacs shell script; I assume he was talking about this. Emacs 29 will probably make those obsolete. #3 pointed out that Chemacs can mess with some configs. He mentioned an “early init file“, which I had never heard of. I guess his work with Crafted Emacs forces him to deal with corner cases that most users never deal with.

#6 asked about blogging. He uses org2blog to publish to WordPress. I also blog with WordPress. I write the post in Org, use org-export-dispatch to export to HTML, and then copy and past the HTML into Classic Editor. I hate the Gutenberg editor, and based on the reviews for the Classic Editor plugin, I am not alone; instead of calling the new editor Gutenberg, they should have called it Torquemada. If the Classic Editor plugin gets discontinued, I might go with JBake. #3 uses WriteFreely. #4 pointed out Hugo supports Org, and mentioned write.as and Keybase. #6 also looking at a way to display inline pdfs in Org.

#3 pointed out that Emacs runs on Android. At first I don’t see the point, but #3 mentioned that Chromebooks can run Android.

#4 gave a link to a reg-ex debugger, but this site might not work with Emacs regular expressions. So with Emacs regex, you wind up having three problems. You can find a page on Emacs regular expressions here, Perl here, and Java here. There is a free regex tester that is a part of a group of sties called Dan’s Tools. A few people shared some regex horror stories. (Is “regex horror story” redundant?)

#5 had his cat say hello. #3 mentioned his dogs. I asked if there are any cat people in Oklahoma, and #3 said there are, but it is mostly a dog state.

#4 asked about Zile, GNU’s other configurable editor. Per GNU, it means “Zile Implements Lua Editors” and also “Zile is Lossy Emacs“. I guess it can be used to run a Lua editor names Zz, or an Emacs clone called Zemacs. This led to talk of other Emacsen. Many of them have fallen away, but they were specific platforms that no longer exist. Linus Torvalds uses MicroEMACS, which was last released in 1996. The source to Gosling Emacs was released recently (Github link here, Hacker News link here). #5 gave a rundown of Gosling emacs: Gosling put a wrapper around the TECO language, sold it to a company that charged for it, RMS got mad and rewrote Gosling’s Lisp.

#3 pointed out RMS does not use external packages, per RMS’s EmacsConf talk. He uses VC, and sees no point in integrating Magit into Emacs. To be fair, per the Emacs docs VC can interface with other version control systems in addition to Git.

The converstation turned to Emacs code: a lot of it is forgotten and hasn’t been looked at or changed in a long time, and that there are a lot of features people do not know about. #3 said that working on Crafted Emacs led him to find features that he had never heard of. One feature that was mentioned was Whitespace mode (see here and here). #3 and #4 also talked about proced, which can manage processes. There is no mention of it in the Emacs documentation. There is an article about it on Mastering Emacs and an article and and article here with discussion here. The source code is here, and mirrored here. You have to invoke it old-school with M-x proced. As far as I know, there is no key chord for it.

Like Dired, there are some commands you can run in the buffer:


(n)ext, (p)revious, (m)ark, (u)nmark, (k)ill, (q)uit (type ? for more help)

Using M-x describe-bindings in the proced buffer, I was able to find the proced functions:

Key Binding
RET proced-refine
C-n next-line
C-p previous-line
SPC next-line
0 .. 9 digit-argument
< beginning-of-buffer
> end-of-buffer
? proced-help
C proced-mark-children
F proced-format-interactive
M proced-mark-all
P proced-mark-parents
T proced-toggle-tree
U proced-unmark-all
d proced-mark
f proced-filter-interactive
g revert-buffer
h describe-mode
k proced-send-signal
m proced-mark
n next-line
o proced-omit-processes
p previous-line
q quit-window
r proced-renice
s Prefix Command
t proced-toggle-marks
u proced-unmark
x proced-send-signal
DEL proced-unmark-backward
S-SPC previous-line
<down> next-line
<header-line> Prefix Command
<mouse-2> proced-refine
<remap> Prefix Command
<up> previous-line
<remap> <advertised-undo> proced-undo
<remap> <undo> proced-undo
<header-line> <mouse-1> proced-sort-header
<header-line> <mouse-2> proced-sort-header
s S proced-sort-interactive
s c proced-sort-pcpu
s m proced-sort-pmem
s p proced-sort-pid
s s proced-sort-start
s t proced-sort-time
s u proced-sort-user

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 Grec 224, an 11th-century manuscript housed at the National Library of France; image 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.

I Moved to Codeberg

I have moved my repos on Github to Codeberg.

I do not care if I sound like I am stuck in the 1990s, but I never liked Microsoft, and I never trusted them. I do not care how independent MS says Github is. If I can avoid using anything MS and use an alternative instead, I will.

I am tired of having to use Windows just because everybody else uses Windows. It is like Javascript: It is a status quo that everybody defends, nobody actually chose, and very few actually like. I am tired of having to use it just because everybody else uses it. Using MS technologies makes people stupid [Note 1]. Try explaining a BPM server (like Camunda or jBPM) to “business people”. They get the concept until they ask how it integrates with Office and you tell them it does not. Then for some reason a concept they understood five seconds ago is beyond them. If you could solve any problem by putting another spreadsheet onto Sharepit, the world would not have any problems. Yet here we are.

And then there is Copilot. I refuse to work for free for one of the wealthiest companies on the planet. If Bill needs more lawyers to deal with the Epstein fallout, that is not my problem [Note 2]. Maybe he can sell some of the farmland he is buying. Not only are they stealing your work, they are putting you at legal risk: Copilot ignores licenses, and just gives you what it barfs up.

ChatGPT also ignores licensing issues, and insists it is never wrong. Sounds like Bill Gates and Microsoft to me. For anyone looking at ChatGPT: Do not touch it. It is made by OpenAI: which is owned by (among others) Microsoft, Elon Musk and Peter Thiel. If you use it, you will just make rich jerks richer.

I started looking into alternatives after the Software Freedom Conservancy had a post on getting off of Github (blog post here, page here, post on Hacker News here). As the blog post points out, Copilot is not using MS code for input. Why not? MS owns them. If MS says there are licensing issues, all the repo owners on Github could say the same thing. Rules for thee, not for me. No thank you.

And now Windows is just a big spyware and ad machine, despite Microsoft having a captive audience and being one of the largest companies in the world (see article here). Yes, you can turn it off, but you should not have to. If Microsoft is so great, why do they constantly have to push their software on you? It’s my machine; I should be able to do what I want, and not see what I do not want. It is possible I am running something that is sending some telemetry somewhere, but I do not have to be perfectly consistent to want Microsoft out of my life.

So I moved all my repos over. I now have nothing on Github. I also moved where I store the repos on my own machine, so I had to change some paths in some config files. My Emacs config has a lot of URLs in comments for other packages, and there are a LOT of things on Github. The open source world is highly dependent on Github. At the next Austin Golang meetup I will ask how Go manages packages. I see a lot of github.com URLs in Go repos. For Java, I think the central Maven repository is run by a company called Sonatype, but I think the Apache Foundation can take it over if Sonatype goes down or goes under (see this article). Granted, if Apache runs Maven, that gives IBM more influence; can you see why I am looking beyond Java?

To migrate to Codeberg, you can follow the directions on Codeberg here. They even have a page that lets you migrate from another git repository, like Github, as long as the other repo is publicly web-accessible. You just put in the other repo’s URL, and it does it all for you. Granted, I have no idea what is to stop a person from migrating someone else’s repo and claiming it as their own. But I only did mine.

I like the fact that they display my name as “EMacAdie” and not “emacadie” like Github. I guess there are some people in Germany working on Codeberg with case sensitive names. Thanks, Herr von Cognizant, whoever you are.

Codeberg says they have enough money for ten years. Still, I might donate to them in the future. Unless the Emacs Mastodon server needs money.

You decided to bend over
Because Bill and Steve told ya
The new guy is named Satya
And if you're still on Windows, he's got ya

You’re welcome.

Note 1: You don’t think contact with MS makes people stupid? Look at his ex-wife. She got big fancy degrees, but then she married Bill Gates.

Most people never really admired Gates. People just liked his money.

On the other hand, he is not one of the many billionaires trying to overturn a legitimate election and cares about energy and climate change, so I guess he’s not ALL bad. Just 99% bad.

Note 2: To give you an idea of Bill Gates’ ethics and intelligence: His first personal investment manager was a convicted felon (see Forbes article here). Gates hired him post-conviction. Even though anyone with two brain cells could tell you that is as bad an idea as it looks, he had to be told by his mother this was a bad idea. He was an adult at the time. I wonder if she asked for a refund from the private school she sent him to. How do such stupid people get to be wealthy? Also: The first manager has not done so well since he got replaced per the linked article.

Image from the Munich Serbian Psalter, a 14-th century manuscript housed in the Bavarian State Library, allowed under CC0 1.0 license.

2023-02 Austin Emacs Meetup

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

#1 was one of the organizers. He used to live in Austin, but now lives in East Texas.
#2 was a developer near Dallas. He was a power user of IntelliJ, but now uses Doom.
#3 was one of our developers in OKC (Still the Artist Known as Number Three).
#4 did not say much; their name was unfamiliar to me.
#5 was one of the organizers, and formerly worked for the City of Austin.
#6 was a guy from San Francisco, who also did not say much.
#7 was our professor from OKC.
#8 was from Seattle. I think he attended in 2022-12, and was trying to transition from VS Code to Emacs.
There was a #9 and a #10, but they did not say anything while I was on.

I joined a bit late, and there was a lot of talk about running a meeting. I think #6 is involved in the Emacs group in San Francisco. He said that running a meeting is a lot of work. Someone mentioned recording the meeting, and that was shot down. I think a lot of people did not like being recorded unfiltered. Granted, Emacs users are even more intelligent, sophisticated and attractive than other IT folks, even readers of Tales From the Jar Side. EmacsSF does have a Youtube channel, but there are some gaps. I do not remember if #6 said why they stopped recording or if he had any part in the decision. Maybe they just got sick of doing it.

#8 said it is hard to get into Emacs. #1 recommended resources: YouTube, Sacha Chua, ChatGPT. There were some suggestions about how to discover things in Emacs: C-h o (which runs describe-symbol), info pages. #7 mentioned the Emacs Buddy initiative system: you can connect with an experienced Emacs user. You can find the web page about it here, and an EmacsConf22 talk about it here. I have still not gotten around to watching the videos from prior EmacsConf years.

A lot of the meeting was #2 and #3 sharing their screens showing the rest of us how they use Emacs and Org to manage REST requests. They use different languages inside the Org files to make the requests and to process the results. I have to admit sometimes I was a bit lost during their demos; their Emacs-fu is very powerful.

They both mentioned an Emacs package called verb to help manage requests. #2 uses shell, awk and Python to make the requests, then transforms the JSON result into edn (extensible data notation) (pronounced like “Garden of edn”) and works with it using Clojure in a REPL inside Emacs. He also changed his file to make a request with curl. #3 had an elisp function inside javascript to populate his JSON request.

#3 talked about verb using the header line. I honestly have never heard of the header line. The mode line is the line at the bottom, and every config has that; the header line is like a mode line at the top. I think most configs do not use it or disable it. Prelude does not have it. My config (based on an Emacs config for Clojure by Daniel Higginbotham, aka flyingmachine) does not have it either (see Note 1 below). I think the mode line has always been in Emacs, and the header line was just added a few years ago.

#3 also mentioned which-key: per its web page it is a package “that displays available keybindings in popup”. #3 says lives in Org mode, and uses it to keep notes for meetings; he is an inspiration to us all.

#8 said he loves and hates VS Code. MSCode is easy to use, and he said he was having a hard time getting into Emacs. I think he might be trying to do too much at once with Emacs. #1 mentioned you do not have to open PDFs in Emacs if you do not want to. #2: said many people see Emacs as an editor, tool, or IDE, and while it is those things, ultimately it is a Lisp REPL. I wish I heard what he said after that, but then my power went out. Perhaps next time he will complete the thought again.

You’re welcome.

Note 1: Prelude and flyingmachine’s Emacs config may have changed since I last downloaded them. My version of Prelude is from a year ago, and I have been altering flyingmachine’s config from about three years ago.

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 Mungi Gospels, an 11th-century Armenian manuscript housed at the Matenadaran Institute of Ancient Manuscripts in Yerevan; image from Wikimedia, assumed allowed under public domain.

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

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

You’re welcome.


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.

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