I recently had to debug a whole bunch of code that manipuates pointers, so I need to stare at random things like 0x7f7d6ab2c0c0, like so:

mkClosure_capture0_args0 (0x7f079ae2a0c0:) -> 0x556b95a23350:mkClosure_capture0_args0 (0x7f079ae2a0e0:) -> 0x556b95a3f3e0:mkClosure_capture0_args2 (0x7f079ae2a000:,  0x556b95a23350:, 0x556b95a3f3e0:) -> 0x556b95a232e0:evalClosure (0x556b95a232e0:)  ⋮evalClosure (0x556b95a23350:)  ⋮  ⋮mkConstructor1 (MkSimpleInt, 0x1) -> 0x556b9596c0b0:  ⋮=>0x556b9596c0b0:  ⋮isConstructorTagEq (0x556b9596c0b0:MkSimpleInt, MkSimpleInt) -> 1  ⋮extractConstructorArg  0 -> 0x1:  ⋮evalClosure (0x556b95a3f3e0:)  ⋮  ⋮mkConstructor1 (MkSimpleInt, 0x2) -> 0x556b95a23190:  ⋮=>0x556b95a23190:  ⋮isConstructorTagEq (0x556b95a23190:MkSimpleInt, MkSimpleInt) -> 1  ⋮extractConstructorArg  0 -> 0x2:  ⋮mkConstructor1 (MkSimpleInt, 0x3) -> 0x556b95902a30:=>0x556b95902a30:

I got annoyed because it's hard to spot differences across numbers. So I wrote a small '''algorithm''' that converts this into something pronounceable:

char *getPronouncableNum(size_t N) {     const char *cs = "bcdfghjklmnpqrstvwxzy";     const char *vs = "aeiou";     size_t ncs = strlen(cs); size_t nvs = strlen(vs);     char buf[1024]; char *out = buf;     int i = 0;     while(N > 0) {         const size_t icur = N % (ncs * nvs);         *out++ = cs[icur%ncs]; *out++ = vs[(icur/ncs) % nvs];         N /= ncs*nvs;         if (N > 0 && !(++i % 2)) { *out++ = '-'; }     }     *out = 0;     return strdup(buf);};

which gives me the much more pleasant output:

mkClosure_capture0_args0 (0x7fbf49b6d0c0:cisi-jece-xecu-yu)  -> 0x561c5f11f9d0:suje-zoni-ciho-komkClosure_capture0_args0 (0x7fbf49b6d0e0:qosi-jece-xecu-yu)  -> 0x561c5f12f1b0:leda-guni-ciho-komkClosure_capture0_args2 (0x7fbf49b6d000:ziqi-jece-xecu-yu,  0x561c5f11f9d0:suje-zoni-ciho-ko,  0x561c5f12f1b0:leda-guni-ciho-ko)    -> 0x561c5f11f960:kuhe-zoni-ciho-koevalClosure (0x561c5f11f960:kuhe-zoni-ciho-ko)  ⋮evalClosure (0x561c5f11f9d0:suje-zoni-ciho-ko)  ⋮  ⋮mkConstructor1 (MkSimpleInt, 0x1) -> 0x561c5f129c10:qifa-duni-ciho-ko  ⋮=>0x561c5f129c10:qifa-duni-ciho-ko  ⋮isConstructorTagEq (0x561c5f129c10:MkSimpleInt, MkSimpleInt) -> 1  ⋮extractConstructorArg  0 -> 0x1:ca  ⋮evalClosure (0x561c5f12f1b0:leda-guni-ciho-ko)  ⋮  ⋮mkConstructor1 (MkSimpleInt, 0x2) -> 0x561c5f120200:nuhi-zoni-ciho-ko  ⋮=>0x561c5f120200:nuhi-zoni-ciho-ko  ⋮isConstructorTagEq (0x561c5f120200:MkSimpleInt, MkSimpleInt) -> 1  ⋮extractConstructorArg  0 -> 0x2:da  ⋮mkConstructor1 (MkSimpleInt, 0x3) -> 0x561c5f100010:kuqi-koni-ciho-ko=>0x561c5f100010:kuqi-koni-ciho-ko

The strings of the form ziqi-jece-xecu-yu makes it way easier to see control flow. I can also see if two pointers are close, based on shared suffixes: ciho-ko is shared, which means the numbers are themselves close.