Today I Learned

A Hashrocket project

Psql Watch

The Postgres REPL psql in includes a \watch command that repeatedly executes a command every n seconds. Here’s the official description:

\watch [ seconds ] Repeatedly execute the current query buffer (like \g) until interrupted or the query fails. Wait the specified number of seconds (default 2) between executions.

It executes the current query buffer, which you can print with \p:

tilex_dev=# \p
select title from posts order by inserted_at desc limit 1;

Run \watch and tail your query result as it changes:

tilex_dev=# \watch
Sun Jul 29 17:06:13 2018 (every 2s)

        title
----------------------
 Psql Watch
(1 row)

Time: 2.175 ms

source

Two ways to access emojis in Windows 10

Include an emoji via keyboard shortcut while typing in Windows 10 by using the Windows key together with the period key. Windows key + semicolon is also a valid shortcut.

No keyboard handy? In the right-hand part of the taskbar, there is a small keyboard icon. If that is not visible, you can enable it by right-clicking on the taskbar.

Clicking this taskbar icon enables the touch keyboard, which includes a smiley key that takes you to to the emoji list.

Now, you can express yourself. 🤠

Be specific in styled components with `&`

input[type="number"] has styles applied at a global level. I want to override some of those styles

a more specific selector takes precedence over a less specific one

So if I just define some css that is:

.myCoolInput {
  background-color: green;
}

It won’t override the more specific input[type="number"] selector’s background-color: pink.

So this styled component I have is wrong.

const GreenInput = styled.input`
    background-color: green;
`

render (
  <GreenInput type=["number"]/>
);

I need to use the & and move the className attribute to get more specificity for my style.

const GreenInput = styled(NumberInput)`
  input& {
      background-color: green;
  }
`

This outputs:

input.<GreenInputClassId> {
  background-color: green;
}

This selector has the same specificity as input[type="number"] but is declared later, so takes precedence.

Don't forget the `className` prop!!

So this is normal styled-component stuff:

const RedDiv = styled.div`
  color: red;
`

which outputs

<div class='<someClassId>'>
</div>

.someClassId { color: red; } 

And then you can wrap that div

const BlueDiv = styled(RedDiv)`
  color: blue;
`

which outputs:

<div class='<someClassId>'>
</div>

.someClassId { color: blue; } 

And you can also style existing components. If I have the component:

const CoolComponent = (props) => {
  render(
    <div></div>;
  )
};

const PurpleDiv = styled(CoolComponent)`
  color: purple; 
`

That outputs

<div></div>

It’s not purple!!

That’s because if you want to style non-styled-component components with styled-components then you have to use the className prop to set the class onto your element.

const CoolComponent = ({className}) => {
  render(
    <div className={className}></div>;
  )
};

Which outputs

<div class='<someClassId>'>
</div>

.someClassId { color: purple; } 

React-Router Location Hash #️⃣

Want to do cool things with the hash element of a URL, using React-Router? You can!

React-Router’s location contains a hash key:

{
  key: 'ac3df4',
  pathname: '/somewhere'
  search: '?some=search-string',
  hash: '#hereiam',
  state: {
    [userDefined]: true
  }
}

Anywhere location is available, pull off hash (defaults to an empty string) and have fun!

`classList.toggle()` 2nd arg doesn't work in IE11

Months and months ago I faced a situation where I had to remove an element’s class outside of the react render tree.

Because toggle was being used to add the class in the code previously I used to toggle to remove it, and to ensure that it was removed and not added I used the second argument which would force it to be removed.

Today I learned that the second argument does not work in IE11.

In general, using toggle is an anti-pattern because it’s non deterministic, it depends on state that is unavailable to you when writing the code.

There are methods add and remove for classList, they have some IE11 edges too, but are more explicit about their intention.

See more info here.

Preserve whitespace while joining in Vim

I have this text:

abc
   def

There are 3 spaces on the line before “def” and I want to make sure that space is preserved.

I usually join with J in normal mode. There is, however, a :join command that behaves the same way except you can follow it with an optional !.

By using the abbreviation it is shortened to:

:j!

And the result I get is:

abc   def

Space preserved!

See :help :join for more information.

Reasonml Interpolation

Reasonml doesn’t support string interpolation in it’s primary string type, but for javascript interop they expose a

special tag quoted string interpolation

Using the {j| |j} operator and a $ for the variable.

It looks like this:

let name = "Chris";
let greeting = {j|Hi there $name|j};

Parens around the value also works:

let greeting = {j|Hi there $(name)|j};

Play with it more here

For When That Escape Key Is Hard To Reach

Using Vim to its full potential requires using just about every key on the keyboard. Of all those keys, the escape key is particularly important. It’s how you get back to normal node.

If you find the escape key hard to reach or your laptop vendor decided you don’t need it anymore, what’s the alternative?

There is a built-in alternative:

Ctrl-[

Note: If your <Esc> key is hard to hit on your keyboard, train yourself to use CTRL-[.

See :help Ctrl-[ for more details.

source

Another pipe operator, the fast pipe `|.`

Reasonml has a pipe operator |> that places a value into the first positional argument, like so:

> let result = 4 |> String.sub("abcdefg", 1)
"bcde"

This is different than the same operator in Elixir, but I get it. I just learned however that Reasonml has another pipe operator, the fast pipe |. that places the value into the first argument of the function, like this:

> let result = "abcdefg" |. String.sub(2, 4)
"cdef"

This seems to be evolving functionality with some special cases that you can learn more about in the bucklescript docs.

Stream A File Line By Line In ReasonML

We can use the Stream module in ReasonML to read a file getting each line on demand. Doing this requires two key insights. First, we can open a file as an input channel. Second, we can turn an input channel into a stream using Stream.from.

let file_in_channel = Pervasives.open_in('file.txt');

let file_stream =
  Stream.from(_i => {
    switch(Pervasives.input_line(file_in_channel)) {
    | line => Some(line)
    | exception(End_of_file) => None
    };
  });

file_stream |> Stream.iter(line => do_something(line));

The Pervasives module (which is open by default and is only prefixed above so as to be explicit) allows us to open the named file as an input channel with open_in. It also allows us to read lines off that file with input_line. We use Stream.from to create a custom stream that consumes the input channel line by line using input_line. We either get some line or we hit the end of the file. Lastly, we can do whatever we want with the stream, such as iterate over it.

See the docs for Pervasives and Stream for more details.

No-op reducer in Reason React

Generally in ReasonReact if you have a reducer component that component will have a reducer function that looks like this:

reducer: (action, state) => {
    switch(action) {
    | Change(newValue) =>
        ReasonReact.Update({value: newValue})
  }
}

ReasonReact.Update is a variant that constructs with a state value.

Another variant you can return from the reducer function is ReasonReact.NoUpdate, allowing you to not update the state if circumstances do not warrant it.

reducer: (action, state) => {
    switch(action) {
    | Change("UghWat") =>
        ReasonReact.NoUpdate
    | Change(newValue) =>
        ReasonReact.Update({value: newValue})
  }
}

Not if the user enters “UghWat” we won’t update the state with that useless value.

Change Styled Component via Parent state

Styled Components has been changing the way I think about CSS and styling. Including the css in with the component makes sense, you don’t have to hunt down styles or wonder what unravels when you start changing styles in a heavily nested scss document.

Today I learned that you can reference styled components in other components to allow parents to change the state of their children.

<Parent>
    <Thing/>
</Parent>

When a user hovers over Thing’s parent, I want to change it’s color from blue to red. I can do that by interpolating Thing into Parent.

const Thing = styled.div`
  color: blue;
`
const Parent = styled.div`
  &:hover {
    ${Thing} {
      color: red 
    }
  }
`

Add The VSCode CLI To Your Path

Visual Studio Code has a command line tool that can do a bunch of things. Perhaps the most common is opening up the current directory from the command line.

First, you need to add code to your path. This can be done from within Code itself.

Hit Cmd+Shift+p to pop open the command palette. Then start typing Shell Command ... until the Shell Command: Install "code" command in shell PATH option appears. Select this and Code will add code to your path.

Try code . to open the current directory or run code --help for more details on what’s available.

Making Things Mutable In ReasonML

In ReasonML, things that we create with let are immutable — which means that we can’t change them.

let num = 5;

Once num is bound to 5 it is stuck with that value for the duration of it’s scope.

ReasonML doesn’t completely restrict us to immutability though. The ref construct allows us to bind a variable to a sort of box that holds a value. We can then look in the box and change what is in the box.

let num = ref(5); /* put 5 in the box */

Js.log(num^); /* use `^` to look in the box */

num := 3; /* remove 5, put 3 in the box */

We use ref to bind our variable to a box with some initial value. The := assignment operator allows us to change what’s in the box. Anytime we want to refer to what’s in the box, we postfix our variable with ^.

Also of note: while list instances are not mutable, array instances are.

source

Reformat Reason code with the `--in-place` option

The ability to format code automatically and in a standardized way is an accepted part of writing code today. I use a format tool in javascript (w/prettier), in golang, and in Elixir.

Reasonml has it’s own format tool, refmt which is installable via npm:

npm install -g reason-cli@3.2.0-darwin

At the command line you should use the --in-place option to overwrite the specified file with the formatted code output by the tool.

> refmt --in-place src/App.re

Otherwise refmt writes to stdout, but rerouting that stdout to the same file will just truncate the file.

Limiting Regex Alternation

The pipe operator (|, or the ‘alternation’ operator) is the ‘or’ of Regexes. Here’s a JavaScript example:

> 'hello'.match(/^hello|goodbye$/)
[ 'hello', index: 0, input: 'hello' ]
> 'goodbye'.match(/^hello|goodbye$/)
[ 'goodbye', index: 0, input: 'goodbye' ]

You might think this Regex can be true in two situations: the string is 'hello' or 'goodbye', because the Regex includes the start-of-line and end-of-line characters (^ and $). I have bad news:

> 'hello there'.match(/^hello|goodbye$/)
[ 'hello', index: 0, input: 'hello there' ]

What’s going on here? This is truthy because the | has a low precedence. It’s evaluated last, after other characters like () and ?. To put this Regex into words (I think): “does this string match anything before the pipe (including the start of line character), or anything after the pipe (including the end of line character)?”. The expression matches on ^hello and ignores anything after that.

We can contain it by telling the pipe to stop evaluating. Parentheses work because they have a higher order of precedence. Here’s our new Regex:

> 'hello there'.match(/^(hello|goodbye)$/)
null
> 'say goodbye'.match(/^(hello|goodbye)$/)
null

Bottom line: when using the pipe the way I’ve described, use parentheses.

Single arg pattern matching with the fun operator

Reasonml has pattern matching in specific syntaxes and one of those syntaxes is the fun operator which helps you define multiple patterns for single argument functions.

let something = 
  fun
  | "hello" => "world"
  | "busta" => "rhymes"
  | x => "something else"

when called:

something("hello")
/* results in "world" */

This doesn’t work for multiple arguments however, so when you see something like this:

let add =
  fun
  | (1, 1) => 3
  | (x, y) => x + y;

just remember that the single argument in this case is a tuple, called like this add((1, 3)).

Generate Starter ReasonML Projects With bsb

With the latest bs-platform installed, you should have access to the bsb command which contains a number of options — including -themes.

$ bsb -themes
Available themes:
basic
basic-reason
generator
minimal
node
react

This is a list of themes (read: templates) that can be used to generate a starter project.

For example, if you’d like a basic project structure geared toward writing Reason, run the following:

$ bsb -init my-project -theme basic-reason

Or if you’d like to get started with reason-react, give this a try:

$ bsb -init my-reason-react-project -theme react

source

List Stats For A File

The ls command is good for listing files. Tacking on the -la flags gives you a bunch of info about each of the listed files. To get even more info, we can use the stat command.

$ stat README.md
16777220 143994676 -rw-r--r-- 1 jbranchaud staff 0 53557 "Jul 14 14:53:44 2018" "Jul 10 14:54:39 2018" "Jul 10 14:54:39 2018" "Jul 10 14:54:39 2018" 4096 112 0 README.md

That’s definitely more info, but it is unlabeled and a lot to parse. We can improve the output with the -x flag.

$ stat -x README.md
  File: "README.md"
  Size: 53557        FileType: Regular File
  Mode: (0644/-rw-r--r--)         Uid: (  501/jbranchaud)  Gid: (   20/   staff)
Device: 1,4   Inode: 143994676    Links: 1
Access: Sat Jul 14 14:53:44 2018
Modify: Tue Jul 10 14:54:39 2018
Change: Tue Jul 10 14:54:39 2018

See man stat for more details.

source

Dropping Commits With Git Rebase

I’ve been warned enough times about the potential dangers of git reset --hard ... that I always second guess myself as I type it out. Is it git reset --hard HEAD or git reset --hard HEAD~?

If the working directory and index are clean, then there is another way to remove commits. A way that gives me more confidence about what exactly is being removed.

Doing an interactive rebase gives you a number of options. One of those options is d (which stands for drop).

$ git rebase -i master

This pulls up an interactive rebase with all commits going back to what is on master — great for when working from a feature branch.

pick 71ed173 Add Create A Stream From An Array as a reasonml til
pick 80ac8d3 Add some clarity by distinguishing var names
d 4f06c32 Add Data Structures With Self-Referential Types as a reasonml til
d 01a0e75 Fix the name of this file

Adding d next to the commits you want to get rid of and saving will drop those commits. The great part is that there is zero ambiguity about which ones are being dropped.

h/t Jake Worth

Log Into Windows 10 Without A Keyboard

Sometimes you might need to log into a Windows machine without a keyboard. A damaged keyboard, non-functioning ports for connecting an external keyboard, or a faulty keyboard driver could put you in this predicament.

Windows 10 includes an accessibility menu called ‘Ease of Access’, located in the lower righthand corner of the screen. This menu includes an on-screen keyboard. Use it to log in when your keyboard doesn’t work.

Ease of Access

Render a list of jsx elements with ReasonReact

In javascript/jsx I can just create a list of jsx elements and place that list into the jsx.

const thingDivs = things.map((thing) => (
    <div>{thing.name}</div>
)

return (<div>
  {thingDivs}
</div>)

But ReasonReact expects the variable to be of type ReasonReact.reactElement, and so we have to convert the list of jsx nodes we create to an element with the function ReasonReact.array.

let thingDivs = List.map((thing) => 
  <div>ReasonReact.string(thing.name)</div>
, things)

<div className="parentContainer">
  (ReasonReact.array(Array.of_list(thingDivs))
</div>

ReasonReact.array returns a value of type ReasonReact.reactElement.

Data Structures With Self-Referential Types

ReasonML has a powerful type system that allows us to create types that represent all sorts of things. For data structures like linked lists, we need a sort of recursive type, a type that can reference itself — a self-referential type.

Reason’s type system allows us to define types that reference themselves. Combine that with type arguments and variants — we can create a type definition to represents something like a linked list.

type linked_list('a) =
  | Empty
  | Node('a, linked_list('a));

A linked list is a chain of nodes. It can be an empty list, hence the first part of the variant. Otherwise, it is a node that has some data and then points to another linked list (chain of nodes).

The 'a part is a type argument. When creating a linked list, we can decide what type the 'a will be. Here is an int-based linked list:

let my_list: linked_list(int) = Node(25, Node(27, Empty));
/* my_list = [25] => [27] => [] */

Output Emojis 🔥 with ReasonReact

I’m using ReasonReact on top of ReasonML and I’m trying to output some emojis like this:

      <div>(ReasonReact.string("Fire 🔥"))</div>

but the output is:

Fire 🔥

The solution I found is to use the funky looking “Js string” instead of double quotes. The Js string in ReasonML has curlies and pipes with a js in the middle {js| Fire 🔥 |js}

In context it looks like this:

<div>(ReasonReact.string({js| Fire 🔥 |js}))</div>

And now I can see some fire:

Fire 🔥 

Create A Stream From An Array In ReasonML

There are functions in the Stream module for turning a variety of data structures into streams — lists, input channels, etc.

What if you have an array?

The Stream.from function lets you define a function for custom fitting data structures into streams. Let’s take a look:

let pokemons = [| "bulbasaur", "charmander", "squirtle" |];

let poke_stream: Stream.t(string) =
  Stream.from(i =>
    switch (pokemons[i]) {
    | pokemon => Some(pokemon)
    | exception (Invalid_argument("index out of bounds")) => None
    }
  );

The function takes the current index and needs to either return Some('a) with the corresponding value or None if the stream is empty.

With that, we now have a stream on which we can invoke any of the stream functions.

switch (Stream.next(poke_stream)) {
| pokemon => print_endline(Printf.sprintf("Next Pokemon: %s", pokemon))
| exception Stream.Failure => print_endline("No pokemon left")
};

Dynamically Create A Printf String Format

Formatting a string with Printf requires defining a format for that string.

let str = Printf.sprintf("%6s", "dope");
/* str => "  dope" */

The format is the first argument. At compile-time it is interpreted as a format6 type value.

So, what if you want a dynamically created format value? Simply concatenating some strings together won’t do it because then the type will be string and that’s not going to compile.

The Scanf.format_from_string function can help.

let some_num = 6;
let format_str = "%" ++ string_of_int(some_num) ++ "s";
let format = Scanf.format_from_string(format_str, "%s");

let str = Printf.sprintf(format, "dope");
/* str => "  dope" */

We can convert our string that has the appearance of a format into an actual format6 type. To do this, we have to tell format_from_string what types each of the formats is going to have — hence the second argument %s.

source

Break Out Of A While Loop In ReasonML

The while construct is a great way to loop indefinitely. You may eventually want to break out of the loop. For that, you are going to need to invalidate the while condition. One way of going about this is creating a mutable ref, changing it from true to false when a certain condition is met.

let break = ref(true);

while (break^) {
    switch (Unix.readdir(currentDir)) {
    | exception End_of_file => break := false
    | item => print_endline(item);
    }
};

Here we have a mutable ref called break which starts as true. This is our while condition. Its actual value can be referenced by appending the ^ character — hence break^. Once a certain condition is met inside our while block, we can set break to false using the := operator.

The above code snippet can be seen in full details here.

source

git merge --squash

Today I learned a new Git command that’s really useful. git merge --squash takes all the changes from one branch and stages them on top of another branch, ready to be summarized.

Here’s a sample workflow:

$ git checkout -b feature-branch

# Make changes across multiple commits
$ echo 1 > 1.txt
$ git add 1.txt
$ git commit -m 'Add first textfile'
$ echo 2 > 2.txt
$ git add 2.txt
$ git commit -m 'Add second textfile'

# Stage all changes on master
$ git checkout master
$ git merge --squash feature-branch
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   1.txt
        new file:   2.txt

# Summarize
$ git commit -m 'Add files 1 and 2'

This is a fast way to boil down a lot of WIP commits from a feature branch into a single commit on master.

Yanking a Ruby Gem

Yanking (unpublishing) a Ruby gem is something that has to happen from time to time. Maybe your library has a known vulnerability. Maybe you pushed a gem by mistake. Sometimes the best code is no code.

Here’s how you permanently yank a gem published to Rubygems:

$ gem yank <gemname> -v <version>

What are the side effects? If somebody was locked to your exact version, they would not be able to download that package again and would have to upgrade or downgrade. Consider that when deciding whether to yank.

Policy change about gem yank / CLI reference

NODE_OPTIONS without node

I was using storybook and found it had grown so large that its node process would run out of memory before it could start. The downside was that the storybook command did not accept node arguments, like --max_old_space_size=4096

Starting with node 8, node will look for options in an environment variable named NODE_OPTIONS to be interpreted as if they had been specified on the command line before the actual command line (so they can be overridden).

Example to give storybook more memory:

NODE_OPTIONS=--max_old_space_size=4096 storybook start

*References:
https://storybook.js.org
https://nodejs.org/dist/latest-v8.x/docs/api/cli.html#cli_node_options_options

Run One Jest Test

When iterating on a test, I want to run just that test. This is a bit tricky with Jest, but it can be done.

First, tell the test framework to run a file, or a describe block:

 # Test file
$ yarn test path/to/test.test.js

# Describe block
$ yarn test -t 'myAssertiveDescribeBlock'

Then, add .only to your it or test block to run just that example:

it.only('is true', () => {
  // your test here
});

It’s just that easy.

only docs

Install `capybara-webkit`

Turns out capybara-webkit’s has a dependency on qtwebkit which has been removed from the qt library package. You’ll have the pleasure of finding this out when you run into this grand error:

Project ERROR: Unknown module(s) in QT: webkitwidgets

To get around this, you can install a previous version of qt using brew via:

brew install qt@5.5

Now reload your terminal and ensure that you can run qmake. Once that is all good, go ahead and reinstall/bundle capybara-webkit.

Don’t let this stump you for longer than it did me!

Show escaped bash color codes in less #linux

My ls command colors directories and files according to their type and permissions:

ls with color

But when the window is too small to fit the content I pipe the result into less:

less broken

Which cannot correctly parse the escape code from ls and turn them into color. To fix that add -r to the less command:

solution

Notes:

My l alias is gls -F -G --color --group-directories-first -lah (gls is GNU ls)

You can alias less=less -r if you want this to be the default behavior for less.

Vim: Peek horizontally without moving the cursor

Some people like wordwrapping in Vim. Personally I find it annoying for code because it can destroy the meaning of the code in the context that it was formatted in by the original author, making it hard to understand where one line begins and ends.

Turning wordwrap off though, can put you in a situation where you have a line of text slightly longer than the screen can show, and you may want to scroll a bit to the right/left for reference but not move your cursor and lose your position with ^ or $.

Vim allows you to do that with zs and ze mappings.

DEMO demo

Learn more with :h zs or :h ze.

Disable Swipe Navigation For A Specific App On Mac

Mac’s touch pad has a bunch of handy swipe gestures, including swiping two fingers to the left or the right to navigate backward or forward. This particular gesture can be globally enabled and disabled. I find it useful for most apps and a pain in a few apps, such as Google Chrome.

From the terminal we can disable it for a specific app (like Google Chrome):

$ defaults write com.google.Chrome AppleEnableSwipeNavigateWithScrolls -bool FALSE

Restart the target application, in my case Chrome. The left and right swipe navigation will no longer be triggered.

source

Show materialized view definition in #postgresql

PSQL offers a handy \dv function for showing the definition of a view. That function does not seem to work on materialized views.

To see the definition of a materialized view use the following instead:

select pg_get_viewdef('search_documents');

Another option is to use \d+ search_documents which shows both the query and the columns.

(Replace search_documents with the name of your view.)

Hide all other windows on #macOS

Sometimes your desktop gets cluttered with windows of different running applications which makes it difficult to concetrate on the task at hand.

This can also come in handing when sharing your screen or presenting.

Fortunately macOS offers some shortcuts to remediate this common problem:

⌘+h - hide current window

⌘+⌥+h - hide all other windows

⌃+⌘+f - toggle full screen mode

Create A Cancelable Promise With PCancelable

If an async task takes a really long time and we find out in the middle of its execution that we no longer want the results of that activity, it would be nice to be able to cancel that promise. There was a proposal for cancelable promises, but it has since been withdrawn. There is an alternative though.

The p-cancelable package provides a promise wrapper that acts as a cancelable promise.

import PCancelable from 'p-cancelable';

const fetchPromise = new PCancelable((resolve, reject, onCancel) => {
  setTimeout(() => {
    resolve({ ok: true, data: [1, 2, 3] });
  }, 10000);

  onCancel(() => {
    console.log('Promise is being canceled');
  });
});

fetchPromise.then(response => {
  console.log('Promise Resolved: ', response.data);
}).catch(err => {
  console.log('Promise Rejected: ', err);
});

fetchPromise.cancel();

We can create a promise in a familiar manner. We get an additional argument — the onCancel function. This is a function that will be executed if cancel is called before the promises has resolved or rejected. In the above example, the fetchPromise.cancel() line will be invoked before the setTimeout resolves. The promise will be canceled, causing a rejection which will push us into the catch handler.

Execute command from register

Here’s a cool command

:nmap <leader>s :smile<CR>

If you read the above in vim, how would you execute it? You can use the system buffer or tmux copy+paste and paste it to the command line, but that seems very not-vim.

Vim has a :@ command that will execute registry contents. Just put the line into your registry with yy and then call:

:@"

This executes the contents of the default register

`yo` has been replaced by `]op`

vim-unimpaired is a fantastic vim plugin full of utility mappings, many to do with toggling. I have relied heavily on yo to enter insert mode with the paste option set when I want to use the system buffer for pasting.

Recently, the yo mapping has been repurposed. Read the github issue here.

Instead, ]op is the new mapping to put you in insert mode with the paste option set.

The documentation now looks like this:

                                                *[op* *]op* *yop*
A toggle has not been provided for 'paste' because the typical use case of
wrapping of a solitary insertion is inefficient:  You toggle twice, but
you only paste once (YOPO).  Instead, press [op, ]op, or yop to invoke |o|,
|O|, or |0||C| with 'paste' already set.  Leaving insert mode sets 'nopaste'
automatically.

Interval Comparison in JavaScript

Today I learned a bit about interval comparison. It’s something you might recall from grade-school math:

1 < 5 < 10 -- is this true?

Two languages I’ve found that support it are Python and Clojure:

> if (1 < 5 < 10):
...   True
...
True
> (< 1 5 10)
=> true

JavaScript doesn’t support interval comparison, so we have to find another way to check that condition. Here’s one technique:

if (1 < number && number < 10) {
  // do something
}

It’s a wordier that the previous examples, but I think keeping the variable in the middle of the condition stays true to the interval comparison idea.