Header logo.
small hallucinations
homeyearstagsaboutrss

Recently

Table-driven tests

Go has superb built-in support for good programming practices, such as test-driven development. In fact, I'm learning a lot about TDD while learning Go from this book: “Learn Go with Tests”.

If you want to test a bunch of similar inputs and outputs, it's handy to run table-driven tests. What you do is list input values and expected output in an array, then loop through all the test cases.

I found this blog post by Lorenzo Peppoloni quite helpful. It gives examples in both Golang and Python for table-driven tests.


Create your own keyboard layout optimized to reduce finger fatigue

We all push buttons for a living. Most of us spend enormous amount of time with the Qwerty keyboard layout.

I came across this fascinating video about making your own perfect keyboard layout.

By perfect, “adumb” refered to the shortest distance your fingers travel while typing on the keyboard. And the corpus he used to calculate the total distance is arXiv abstracts.

He first randomly generated keyboard layouts then crossed over the best ones. Until after 1000 generations of crossover, the total finger travel distance stopped decreasing.

If I were to make a keyboard layout, I would use a larger corpus that includes English, Swedish, Chinese as typed using Pinyin, and a bunch of programming languages.

On top of the larger number of symbols to rearrange, when you type CJK (and other complex, non-alphabetic) languages, you need to deal with the complicated relationship between the input method and the keyboard layout, especially on Linux.


zsh

For quite a while, I found I couldn't use commands I clearly had installed properly (macOS 12.2).

After some digging, I used an export command in .zshrc file and forgot to include $PATH: when setting the PATH value.

This answer on StackOverflow was helpful.

Using prepared statements & pointers in Golang

I changed the name of this blog to “a study of bugs”. This makes it easier for me to think of what to write about -- bugs, of course.

In MySQL, you can use a question mark (?) in a prepared statement to stand in for a value.

 1func GetUserId(name string) (int, error) {
 2	// (1) - BAD
 3	stmt, err := dbconn.Db.Prepare("SELECT id FROM user WHERE name=\"?\";")
 4	// (2) – GOOD
 5	stmt, err := dbconn.Db.Prepare("SELECT id FROM user WHERE name=?;") 
 6	if err != nil {
 7		log.Fatal(err)
 8	}
 9    defer stmt.Close()
10
11	var userId int
12	err = stmt.QueryRow(name).Scan(&userId) // (3)
13	if err != nil {
14		if err != sql.ErrNoRows {
15			log.Fatal(err)
16		}
17		return 0, err
18	}
19
20	return userId, nil
21}

In the Go code above, dbconn is a connection to a MySQL server. Line 1 defines a prepared statement. And Line 3 queries the table for a row where the name column matches the value of variable name. I assumed the ? in this query would be interpolated with an actual string. I added quotation marks since they are needed around strings in MySQL CLI.

This fails to return anything. Removing the quotation marks solved the problem. The correct code is on Line 2.

I also find Line 3 quite interesting. Here, first, stmt.QueryRow() returns a pointer to a sql.Row object. Its Scan() method then does two fascinating things: a) it sets a “dest” to the output value and b) returns an error if there's an error or nil if there's no error.

Thingy a is quite interesting. In this case, Scan() method will find the memory address of variable userId and write the output value (the user's ID) there.

It feels as if getting the value from a DB is a side effect.

The same goes when you decode a JSON string. In this example, on Line 4, Unmarshal() method unpacks the JSON string and writes it to &animals, the address of animals variable. And this is a side effect. The return value is again, either an error or nil — if there is an error, you simply can't ignore it.

 1var jsonBlob = []byte(`[
 2	{"Name": "Platypus", "Order": "Monotremata"},
 3	{"Name": "Quoll",    "Order": "Dasyuromorphia"}
 4]`)
 5type Animal struct {
 6	Name  string
 7	Order string
 8}
 9var animals []Animal
10err := json.Unmarshal(jsonBlob, &animals) // (4)

“Zulu timezone”

I've been using a pomodoro app that provides a REST API. Through this API, you can query past pomodoros using parameters such as ended_before and ended_after. The response data also contains started_at and ended_at fields.

The values for these fields all look like this: 2022-01-01T07:18:59.000Z. And they are UTC time strings suffixed with a Z. Correctly so, because it indicates this timestring is UTC.

There are fields named local_started_at and local_ended_at. Although those values are clearly not UTC, they are all suffixed with a Z. This confused me a bit at first. (And it seems quite common for people to suffix Z at the end of a time string, regardless of which timezone that time string actually is.)

The default timezone in a Docker container is UTC. The default timezone of AWS is also UTC. (I wrote a script to check just to be sure. But it's actually written somewhere in the documentation.)

Interestingly, Python does not support the letter Z when you try to parse a timestring using datetime.fromisoformat. But if you use +00:00 to mark a zero offset from UTC, it works and a datetime object with timezone info is returned.

 1>>> datetime.fromisoformat('1989-06-04T08:11:25.000Z') # ❌
 2Traceback (most recent call last):
 3  File "<stdin>", line 1, in <module>
 4ValueError: Invalid isoformat string: '1989-06-04T08:11:25.000Z'
 5
 6>>> datetime.fromisoformat('1989-06-04T08:11:25.000+00:00') # ✅
 7datetime.datetime(1989, 6, 4, 8, 11, 25, tzinfo=datetime.timezone.utc)
 8
 9>>> datetime.fromisoformat('1989-06-04T08:11:25.000+02:00') # ✅
10datetime.datetime(1989, 6, 4, 8, 11, 25,
11    tzinfo=datetime.timezone(datetime.timedelta(seconds=7200)))

Now I digress.

Z apparently stands for Zulu in “military timezones”. 25 letters are used to indicate the time difference from UTC. Although the Z timezone means UTC, the timezone where the actual Zulu people live has a offset of +02:00.

There are also India, Lima and Quebec in these military timezone names. Only Quebec indicates the actual timezone used in Quebec as daylight saving time.

A simple demo of how useCallback works

This is how useCallback works.

In the callback defined on line 🙄, the function body increases the count by 1. We set the dependency list to be shallChange. This way, the count will not increase unless shallChange gets a different value.

If you click Count+ multiple times, the count will only increase once. It will increase again after you click Shall Change? butotn.

 1import { useCallback, useState } from 'react';
 2
 3function App() {
 4  const [count, setCount] = useState(0);
 5  const [shallChange, setShallChange] = useState(0);
 6
 7  const setCountCallback = useCallback( // 🙄
 8    ()=> {
 9      setCount(count+1)
10    }, [shallChange]
11  )
12  
13  const onPlusBtnClick = () => {
14    setCountCallback();
15  }
16
17  const onShallChangeClick = () => {
18    setShallChange(shallChange+1)
19  }
20  
21  return (
22    <div>
23        Count: {count}<br />
24        shallChange: {shallChange}<br />
25        <button onClick={onPlusBtnClick}>Count+</button><br />
26        <button onClick={onShallChangeClick}>Shall change?</button>
27    </div>
28  );
29}
30
31export default App;

A bug in CRA and time complexity of list and set operations

Here are a few things I noticed in week 7.

I was creating a React app using create-react-app and ran into this error:

You are running 'create-react-app' 4.0.3, which is behind the latest release (5.0.0).

After googling around I saw a workaround by installing v5 locally.

Yet a better workaround is:

npx [email protected] app-name

Update on Feb 24

Should have read the thread more closely, because clearing npx cache should do: npx clear-npx-cache.


I was reading Ace the Data Science Interview and there's a chapter about coding.

One easy coding problem reads:

Given two arrays, write a function to get the intersection of the two.

I guess to show your ingenuity, you are not supposed to use sets, which are native to Python.

The most naïve idea is to run a doubly nested for loop to check each item in arr_n against each item in arr_m. That way, the time complexity would be O(n*m).

A twist on this idea is to run a for loop to iterate through the shorter array (arr_n) and check if arr_n[i] exists in the longer array (arr_m). By doing so, the time complexity is also O(n*m) because the average case time complexity to find an item in arr_m is O(m). (I had mistakenly thought checking existence of an item costs O(1).)

A simpler solution is using sets. The official solution given in the book first converts the two arrays to sets. Then checks which items in the shorter set exist in the longer set by doing this:

[x for x in set_n if x in set_m] (time complexity O(n))

The time complexity of this solution in total is O(n) + O(m + n), because converting lists to sets costs O(n) too, and we have two arrays here. Less than O(n*m).

At first I wondered why they didn't directly use set intersection. I guess that's because converting the resulting set back to a list would cost another O(len(intersection)).

Method list.sort() vs function sorted()

The more I program, the more I find myself realizing the why of some of the basics.

You can use either a method or a function to sort or reverse a list in Python. One works in-place, meaning the order of items in the original list is changed. The other works on a copy of the list and returns an ordered version of that copy as a new list.

list.sort() and list.reverse() are methods of the list class. They are both verbs, suggesting they take actions on an instance of the list class. And as methods that are internal to a list, they are understandably “allowed” to change the internal order of list items.

sorted() and reversed() are functions external to the list class. They are both adjectives. If a function tampers with the data passed in as an argument, that makes it not a pure function. So in functional programming terms, they are both pure functions.

Week 47

I spent some time last week to get myself more familiar with TypeScript.

Although I'd known the syntax, how to type things (props, components, etc) was at times still confusing. I probably saw more than a bunch of errors thrown at me.

Hopefully I'd love it when I get the hang of it. People on the internet say, all pros and cons considered, it's worth the while.