Convenience Store Woman

Sayaka Murata

-

The me of today would like to believe that the me of 10 years ago, i.e. the perpetually single me, would have felt kinship with Keiko Furukara, the protagonist of this book. She’s happily unattached, and bristles against all of the people around her who disapprove of her menial job and lack of relationship status.

On the other hand, if the me of 10 years ago had taken a different path, had not found a partner, who would the me of today be? I fear I might have ended up like the angry and deeply unpleasant Shiraha, Keiko’s foil. He is basically what we would call an incel, although I’m not sure if that label is meaningful in Japanese culture.

It’s a fine line between accepting yourself despite not fitting in, and blaming the world for not accepting you. I commend this book for putting me on both sides of that line.

storygraph link

I’ve always preferred the more “mainstream” parts of the MCU over the offbeat energy of the Guardians series. This third one is my favourite of the trilogy. Rocket’s backstory is effectively dark and tragic, and especially hard to watch if you’re an animal lover, like me.

letterboxd link

A collection filled with the type of true crime article that I regularly save to Pocket. I probably wouldn’t recommend reading the book cover-to-cover, but I had to because it was a library loan. In my opinion, it would be better enjoyed by cherry-picking an article from time to time, when you’re in the mood for some moral indignation.

But beyond the morbid point-and-judge tone that’s typical of true crime, I do appreciate that Keefe offers empathy and compassion towards his so-called “rogues.” This is especially evident in my favourite article.

“Loaded Gun” explores the past of a mass shooter. Twenty years before the crime, she shot and killed her brother, in what may or may not have been an accident. I especially liked this article because Keefe inserts his first-person ruminations on the ambiguity of what happened, and what the ambiguity means.

storygraph link

Paper Girls

Brian K. Vaughn & Cliff Chiang

-

The Star Trek vision of time travel usually involved restoring the “correct” timeline after some mishap, and preserving the way that things are “supposed” to be. In the time travel story of Paper Girls, the forces that support timeline preservation are painted as villains, and the act of changing history is seen as a charitable act, a sort of temporal wealth redistribution where the advances of the future are shared with the past. I don’t think I agree with the ethics of this position, but it was an interesting inversion of what I’ve seen before.

My favourite parts of the story involved the teenage girls meeting adult versions of themselves. Because of my age, I identified more with the “guest stars” than the main cast, and found myself wondering what I would say to my younger self. I kind of wish that Paper Girls had offered more answers to this question, beyond “Keep in touch with your friends.”

storygraph link

I was a fan of the TV show The Good Place, and this book dives deeper into the philosophical concepts behind that show’s exploration of morality. The subject matter is pretty dry and basic (I was familiar with much of it from taking a 100-level philosophy course in university), but the author, who also created and ran the TV show, imbues his sitcom-honed sense of humour into it. A bonus if you’re listening to the audiobook: the Good Place cast comes along for the ride; a highlight is Ted Danson’s dramatic reading of chapter titles.

storygraph link

Recently, I’ve been working on some social and gamification features in a web application. I decided to use a backend service called brainCloud instead of implementing all of the database stuff myself. The brainCloud service stores things like player statistics, leaderboards and social relationships (i.e. who’s friends with who?), and provides an HTTP API to read and write the data. They also provide a Javascript client library to make it easier to work with those APIs.

Without getting into too many specifics, the library follows a callback style for handling asynchronous behaviour. When you call one of their functions, it sends an HTTP request; when the server responds, it executes the code in the callback function that you pass as a parameter. For example,

postScoreToLeaderboard(leaderboardId, score, (resp) => {
	if (resp.successful) {
		// do something with the data
		console.log('We got data!', resp.data);
	} else {
		console.log('We got an error!', resp.error);
	}
});

Personally, I prefer to handle asynchronous code using promises in the await/async style.

A general pattern to follow to convert a callback interface to a promise-based one is to build a Promise object and return it.

(N.B.: For the purposes of this post, I’ll follow the convention of prefixing a function name with an underscore to indicate that it’s a “wrapper” around a brainCloud API function.)

const _postScoreToLeaderboard = (leaderboardId, score) => {
	return new Promise(resolve, reject) => {
		postScoreToLeaderboard(leaderboardId, score, (resp) => {
			if (resp.successful) {
				resolve(resp.data);
			} else {
				reject(resp.error);
			}
		});
	});
};

Now, we can use this function without a callback:

try {
	const data = await _postScoreToLeaderboard(leaderboardId, score);
	// do something with the data
	console.log('We got data!', data);
} catch (e) {
	console.log('We got an error!', e);
}

This works, but there are many functions in the brainCloud API. How can we extend this technique in a generic way?

To illustrate, here’s a couple of other brainCloud methods for gamification:

getLeaderboardTop(leaderboardId, (resp) => {
	// handle response
});

incrementUserStats(statId, value, (resp) => {
	// handle response
});

It would be a lot of repetitive code to wrap each of these in a function:

// TOO MUCH CODE!

const _getLeaderboardTop = (leaderboardId) => {
	return new Promise(resolve, reject) => {
		getLeaderboardTop(leaderboardId, (resp) => {
			// handle response
		})
	});
};

const _incrementUserStats = (statId, value) => {
	return new Promise(resolve, reject) => {
		incrementUserStats(statId, value, (resp) => {
			// handle response
		})
	});
};

To reduce duplication, we can curry the functions.

A brief introduction to currying

If you’re not familiar with the term, currying a function creates a new function with parameters that can be called one by one.

The classic example is a function that adds three numbers together.

const add = (a, b, c) => a + b + c;

// create a curried function
const curriedAdd = curry(add);

const addOne = curriedAdd(1);

// this is equivalent to calling add(1, 2, 3)
const x = addOne(2, 3);

// x is 6

Note that this only returns the final result of 6 if we “use up” all three parameters of the original function. If, instead, you leave the final parameter “unused,” it will return yet another function.

// we have "used up" two out of three parameters
const addSeven = addOne(6);

// this is equivalent to calling add(1, 6, 10)
const y = addSeven(10);

// y is 17

Back to our regularly scheduled programming

With this approach, we can curry the functions from the brainCloud API. For example,

const _incrementUserStats = curry(incrementUserStats);

This particular function has two input parameters, and a third for a callback. If we call the curried version with the two input parameters, it will return a function that accepts a callback and executes the original function once it receives the callback.

const fn = _incrementUserStats('statId', 15);

const handleResponse = (resp) => {
	// do something
};

// this is equivalent to calling 
// incrementUserStats('statId', 15, handleResponse)
fn(handleResponse);

I named the final function fn because this part can be generalized to any API call that takes a callback as its final parameter.

If we create a function that takes a function like fn and calls it, we can then use a Promise-based pattern for making these API calls.

const makeAPICall = (fn) => {
	return new Promise((resolve, reject) => {
		fn((resp) => {
			if (resp.successful) {
				resolve(resp.data);
			} else {
				reject(resp.error);
			}
		});
	});
};

To use this, we call one of the curried functions with all of its parameters except for the final callback parameter, and pass the result to the makeAPICall function.

try {
	const data = await makeAPICall(_incrementUserStats('statId', 15));
	// do something with the result
	console.log('We got data!', data);
} catch (e) {
	console.log('We got an error!', e)
}

Putting it all together:

// curry all of the functions that we want to use
const _postScoreToLeaderboard = curry(postScoreToLeaderboard);
const _getLeaderboardTop = curry(getLeaderboardTop);
const _incrementUserStats = curry(incrementUserStats);

// define the function that receives a callback 
// and executes the original function
const makeAPICall = (fn) => {
	return new Promise((resolve, reject) => {
		fn((resp) => {
			if (resp.successful) {
				resolve(resp);
			} else {
				reject(resp);
			}
		});
	});
};

// now we can make API calls like this
const result1 = await makeAPICall(_postScoreToLeaderboard('id', 100));
const result2 = await makeAPICall(_getLeaderboardTop('id'));
const result3 = await makeAPICall(_incrementUserStats('statId', 15));

As a final note, the curry function itself is available in the popular lodash library of Javascript utilities.

Review: China in Ten Words by Yu Hua

Compare and contrast

After my experience with Beijing Confidential, I felt compelled to expose myself to writing about China by native and contemporary authors. China in Ten Words wasn’t exactly what I was looking for—I wanted more focus on modern China, i.e. what is it like now—but it was still an enlightening read.

Each of the ten essays in this collection is a meditation on a Chinese phrase, which are all shown on the cover, in case you’re too lazy to open to the table of contents.

I was a bit impatient through the first half of the book, which deals with the author’s childhood during the Cultural Revolution in the 1960’s. It covers ground that doesn’t feel new to me because of my time with other books. I wanted him to get to the present day, which he does indeed do towards the second half. I would say that the earlier parts of the book do lay an important foundation for the rest of it, because as a whole, the book can be seen as a compare-and-contrast exercise between the time of Mao’s rule and today.

In one memorable passage, Yu describes an incident during his childhood, when he and some other kids enacted vigilante justice against a man who was illegally trading food stamps. The kids physically assaulted the man, but the man didn’t retaliate; instead, he broke down and cried in remorse.

In contrast, Yu recounts a story from more recent times, where an unlicensed street vendor stabbed and killed a government official who was trying to enforce the rules by shutting down the shop.

Yu sees this as a breakdown in the morality of the country. He seems almost nostalgic for the black-and-white authoritarianism of Mao’s day, as opposed to the everyone-for-themselves mentality of today. He acknowledges the brutality of the past, especially for the violent acts of his youth, but suggests that there was some value in unity, where everyone agrees what’s right and wrong. The man that Yu and the other kids assaulted didn’t retaliate because he knew he was breaking the rules.

So China moved from Mao Zedong’s monochrome era of politics-in-command to Deng Xiaoping’s polychrome era of economics above all. “Better a socialist weed than a capitalist seedling,” we used to say in the Cultural Revolution. Today we can’t tell the difference between what is capitalist and what is socialist—weeds and seedlings come from one and the same plant.

  • Yu Hua

Personally, I’ve seen this attitude from the older generations of my family, who seem to believe that obedience is paramount. I don’t always agree, but I’ve been trying to recognize where they’re coming from. This book has further enlightened me to the differences between the two cultures that I inhabit.

Albert

About Me

Hi! Albert here. Canadian. Chinese.

Writing software since 2001. “Blogging” since 2004. Reading since forever.

You can find me on socials with the links below, or contact me here.