Zubashev Stepan's blog

Parametrized var-functions

type Fn = <T>(a: T) => T;
declare const fn: Fn;

const a = fn<number>; // no "()"
a; // const a: (a: number) => number

It was on the surface all along, but it eluded me.

Pure functions

A little rant about functional programming and pure functions. There is a common delusion about pure functions. People often think that:

  • Pure functions cannot use variables. Only constants.
  • Pure functions cannot run impure functions. Only pure.

These statements make some point. But they are not entirely true. Let me clarify this. The only restrictions that pure functions have are:

  • It must be deterministic, meaning it doesn’t have random behavior. No matter how many times you run it with the same set of arguments — you’ll get the same result.
  • It shouldn’t have any side effects.

What is a side effect? Basically, it’s any change in the world outside of the function. E.g.:

  • Changing the given argument
  • Changing a variable defined outside of the function
  • Mutating any value reachable from outside the function (including parameters) or any value that escapes the function
  • Making a query (database, network, etc.)
  • Reading a file, writing to a file, removing a file, etc.
  • Reading external mutable data
  • etc.

I think you got the concept. Now let’s focus on the word “side” in “side effects”. “Side” means it’s outside of the given function. We don’t have these restrictions for internal state. Yep, a pure function may have mutable internal state.

function pure(a: number, b: number): number {
  let c = a ** b;
  if (c > 100) c %= 100;
  return c - 14;
}

^ this one is pure, even though it mutates c. Just because c is internal to the function — it doesn’t affect or depend on anything outside. We don’t mutate it after we return the value.

Ok, what about running impure functions? It’s a dangerous zone. Because it is very dependent on how exactly impure the impure function. If it’s not deterministic, then we have no chance. So the question is about: what side effects an impure function may have while still allowing us to use it inside a pure function? The answer is: the only side effect it may have is mutating the given arguments (only if they are local to the pure function):

function impure(packet: Packet): void {
  // deterministic and based only on packet
  packet.hash = computeHash(packet); 
}

function pure(id: number, data: Data): Packet {
  const packet: Packet = { id, data };
  impure(packet);
  return packet;
}

Here we clearly run impure in pure. But since impure changes only packet, which is entirely managed by pure, we’re safe.

That’s it.

Read more

TIL: HTMLCollection is alive

TIL: HTMLCollections are live-collection. What does it mean? Look. Imagine you have HTML like this:

<div>
  <br/>
  <br/>
</div>

Then you want to find all its <br/>s:

const collection  = $0.getElementsByTagName('br');

Thus far, everything is obvious, right? Ok, let’s add one more <br/>:

$0.append(document.createElement('br'));

So, now have three of them. Let’s read the same collection element again:

Whoa… It was updated too. Why? HTMLCollection is a “live” collection:

An HTMLCollection in the HTML DOM is live; it is automatically updated when the underlying document is changed. For this reason it is a good idea to make a copy (e.g., using Array.from) to iterate over if adding, moving, or removing nodes.

CSS color filters & black

I ran into an issue. Code like this:

.link:hover {
  filter: brightness(1.5);
}

… worked perfectly on a colored icon but had no effect on a text label. It turned out that the label used the color #000. brightness() is a purely mathematical function that multiplies the R, G, and B values by the given factor. This makes no difference for pure black, since 0 * anything = 0.

Evict password managers

There are multiple password managers like 1pass, KeePass, and LastPass. Most of them have browser extensions. They are great. But they alter the page behavior, including HTML. Sometimes we need to prevent them from doing that.

Many years ago, people just did this:

<input autocomplete="off"

Now it doesn’t work. These extensions don’t respect this attribute. You might have seen this one:

<input data-lpignore="true"

It doesn’t work either (at least not with KeePass)

I came up with a simple and pretty silly solution:

<input type="text" onfocus="event.target.type='password'"

And it works ;-) The extensions don’t care about text-inputs. And they are not smart enough to detect new controls. At least for now.

upd: It helped with KeePass, but didn’t work out with 1Password.