Today I Learned

A Hashrocket project

564 posts by joshbranchaud @jbrancha

Creating A 2D Array

In most languages if I wanted to create a two-dimensional array, I would utilize some nested looping construct to generate columns of rows. The ReasonML Array module abstracts this away.

let grid = Array.make_matrix(10, 10, 0);

grid
|> Array.iter(column => {
  column
  |> Array.iter(cell => {
    print_int(cell);
  });
  print_endline("");
});

/*
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
0000000000
*/

The make_matrix function allows you to specify dimensions of a two-dimensional array with all positions initialized to the same value — that third argument.

Modifying A String With blit_string

ReasonML’s Bytes module has a function called blit_string. This function allows you to copy portions of a string into a destination byte sequence. It is a fairly low-level operation, so you have to provide a source string and provide an offset of that source string to start copying from. You then have to provide a properly sized byte sequence as well as the destination’s starting offset and length of bytes to be copied.

Here is an example of how we can use blit_string to create a copy of the string with the first character removed.

let remove_first_char = (str: string): string => {
  let copy_len = String.length(str) - 1;
  let dst = Bytes.create(copy_len);
  Bytes.blit_string(str, 1, dst, 0, copy_len);
  Bytes.to_string(dst);
};

Notice that once the byte sequence has been copied over, we then need to convert it back into a string.

Generate A Native ReasonML Project With Pesy

Pesy is a CLI utility available from NPM that you can use to generate a ReasonML project that is ready for native compilation. It uses esy for the management of opam packages. It uses Dune for building your library code with the ReasonML and OCaml dependencies.

Assuming you already have pesy installed globally, create a directory for your project and then run:

$ pesy

A project will be generated that is out-of-the-box ready to compile native executables.

Using Optional Labeled Function Args In ReasonML

If you are constructing a function that takes some arguments, but one of those arguments has a reasonable default value, then you can use an optional labeled argument. Labeled arguments are those arguments prefixed with a ~. If you give the argument a default value, then it becomes optional.

let thing = (~a=1, b: int, c: int) => {
  a + b + c;
};

In this case ~a is a labeled argument. It is also optional and will default to 1 if not specified. The other two arguments, b and c, are positional arguments and thus required in order for the function to evaluate.

Here are two ways of using this function either by specifying ~a or excluding it so that it defaults to 1.

thing(~a=2, 1, 1)
|> string_of_int
|> print_endline /* 4 */

thing(1, 1)
|> string_of_int
|> print_endline /* 3 */

See more details here.

Using vim-surround With A Visual Selection

The vim-surround plugin allows you to do a variety of actions that have to do with the surrounding characters of text objects.

The S keystroke allows you to surround a visual selection with the following character.

First, make a visual selection. Then hit S. Then hit a surround character such as ( or [ and the area of text that has been visually selected will be wrapped with the respective surround characters.

Duplicate The Current Tab In Chrome

Sometimes when viewing a page in Chrome, you realize you want to keep that page open but also go back to the previous page to view something else. An easy way of achieving this is to duplicate the current tab and then go back.

To duplicate the current tab hit Cmd+Enter while the focus is on the URL bar.

If the URL bar is not in focus, then first hit Cmd+L to focus followed by Cmd+Enter.

Two Ways To Find An Item In A List With ReasonML

The List module has the typical find function that you’d expect any enumerable type to include. It has a very similar find_opt function as well. The difference is in the return types.

When using List.find you’ll have to deal with the possibility of a Not_found exception.

switch (List.find(item => item.id == id, my_list)) {
| exception Not_found => print_endline("Not found!")
| item => print_endline("Found it: " ++ item.name)
}

The List.find_opt function has a more familiar interface that doesn’t require you to know the details of what exceptions could arise. All you want to know is if it was found or not. This is achieved by having an option('a) return type.

switch (List.find_opt(item => item.id == id, my_list)) {
| None => print_endline("Not found!")
| Some(item) => print_endline("Found it: " ++ item.name)
}

See the List module for more details.

Style A Background With CSS Linear Gradient

The linear-gradient function in its simplest form can be used to style the background of an element with a vertical, linear gradient between two colors.

gradient example See the Pen pQpypW by Josh Branchaud (@jbranchaud) on CodePen.

Here is what the CSS looks like:

.container {
  background: linear-gradient(#00449e, #e66465);
}

The background of any element with the container class will be styled with a linear gradient that transitions from #00449e to #e66465.

Check The Password Confirmation With Yup

The Yup library makes it easy to validate individual values in a JavaScript object. A common situation when implementing a Sign Up form is asking the user to input their password twice and then the app can make sure they match.

To do this, we need the validation of our passwordConfirmation value to reach outside of itself to make a comparison with the password value. This can be done with Yup’s ref function.

import * as Yup from 'yup';

validationSchema: Yup.object({
  password: Yup.string().required('Password is required'),
  passwordConfirmation: Yup.string()
     .oneOf([Yup.ref('password'), null], 'Passwords must match')
});

We are able to reference the value of password with ref. We use the oneOf function to ensure that passwordConfirmation either matches password or if it is left blank and matches null then it passes the validation for the time being. The second argument to oneOf is a custom validation message when this validation fails.

source

Using BCrypt To Create And Check Hashed Passwords

The BCrypt library is used under the hood by gems like Devise in order to work with passwords securely. You can use it to salt and hash a plain text password. You can also use it to check whether an encrypted password matches some input password.

> include BCrypt
=> Object

> encrypted_pass = Password.create('password')
=> "$2a$10$te3Y8wdSXf8/gWDeSP5z9eut7alThnuTvq1SvgQyJ1C57F.qit1uq"

> Password.new(encrypted_pass) == "not_my_pass"
=> false

> Password.new(encrypted_pass) == "password"
=> true

The Password.create method will salt and hash the given password. The resulting encrypted password, if it is an instance of Password, can be directly compared to a string. For good measure, in case the encrypted password is a string, you can wrap it in a call to Password.new to ensure you are working with a Password instance.

Generate Ruby Version and Gemset Files With RVM

RVM, the ruby version manager, is a fairly flexible tool that supports a number of workflows. The rvm utility can be used to generate both a .ruby-version file and a .ruby-gemset file for a given project.

$ rvm --ruby-version use 2.5.3@my_project

This will generate a .ruby-version file in your current project directory that points RVM to the 2.5.3 version of Ruby. It will also create a .ruby-gemset file that RVM will use for managing this project’s gemset.

Get Random Images From Unsplash

Here is an image from unsplash.com.

random images

I don’t know what image I am showing you though. That’s because the URL being sourced for the above image is https://source.unsplash.com/random/1200x600. This tells unsplash to randomly serve us a 1200x600 image. Try refreshing the page and you’ll see that it is different each time. Cool!

This is a bit of a novelty, but could be useful on occasion. The Gatsby docs use it to great effect in a tutorial so as to not get caught up with the details of serving a specific image.

Easy Date Comparison With DayJS

Let’s say my application fetches dates from the server which come back in string form as "YYYY-MM-DD" and I’d like to know if those dates already passed. This can be done easily by wrapping dates in DayJS and using its comparison functions.

import dayjs from 'dayjs';

const today = dayjs(new Date());
const pastDate = dayjs("2018-10-22");
const futureDate = dayjs("2022-01-01");

console.log(pastDate.isBefore(today));
// => true
console.log(futureDate.isBefore(today));
// => false

The dayjs() function can be used to construct DayJS date objects from Date objects and strings. These can then be compared with functions like isBefore() and isAfter().

Make A Block Of Text Respect A New Line

Generally when we fill a div tag full of text, it will display it one long strand irrespective of any line breaks that are included. This is a great default, but not necessarily what we want when we are displaying text from another source, such as our users.

We can convince a block of text to respect new lines by adding a couple CSS properties.

.multiline-text {
  word-wrap: break-word;
  white-space: pre-line;
}

The first rule, word-wrap: break-word, ensures that long lines of text uninterrupted by new lines respect the boundaries of our wrapping element. The second rule, white-space: pre-line, handles squashing of extra white space and respecting of new lines.

See a working example here.

Formik's Validation Schema As A Function

The most straightforward way to use Formik’s validationSchema is to provide it with a Yup object defining your form’s validations.

const MyComponent = withFormik({
  // ...

  validationSchema: yup.object().shape({
    email: yup.string().required(),
    feedback: yup.string().required(),
  }),
  
  // ...
})(MyForm);

There may be a point at which you need access to the props being passed to MyComponent in order to construct the proper set of validations. Formik supports this by allowing validationSchema to be a function.

const MyComponent = withFormik({
  // ...

  validationSchema: (props) => {
    let emailSchema;
    if(props.allowAnonymous) {
      emailSchema = yup.string();
    } else {
      emailSchema = yup.string().required();
    }

    return yup.object().shape({
      email: emailSchema,
      feedback: yup.string().required(),
    });
  },
  
  // ...
})(MyForm);

When validationSchema is a function, its first argument is the set of props passed to that component.

Convert A String To A Timestamp In PostgreSQL

If you have a string that represents a point in time, there are a couple ways that you can convert it to a PostgreSQL timestamptz value.

If the string is in ISO 8601 format, then it can be simply cast to timestamptz.

> select '2018-10-24'::timestamptz;
      timestamptz
------------------------
 2018-10-24 00:00:00-05

A more general purpose approach is to use the to_timestamp function.

> select to_timestamp('2018-10-24', 'YYYY-MM-DD');
      to_timestamp
------------------------
 2018-10-24 00:00:00-05

The first argument is our string-to-be-converted in whatever format. The second argument is another string describing in what format that string is.

Note: Both of these approaches produce a timestamptz value.

Compile ReasonML With An OCaml Package Using Dune

In Compile Reason To Native With Dune, I showed how to compile a basic ReasonML file as a native executable using Dune.

Any non-trivial program will likely involve pulling in an OCaml dependency. For example, you may want to pull in Lwt. Assuming this package is available, whether you’ve manually downloaded it via opam or used something like esy, you’ll want to let Dune know that Lwt is an available library.

;; dune
(executable
 (name hello_reason)
 (libraries lwt lwt.unix))

The modules in the Lwt package will now be globally available to your Reason code.

let () = {
  Lwt_main.run(
    Lwt_io.printf("Hello, Reason!\n")
  );
};

When Dune builds your code, it will include and compile Lwt.

See a full example here.

Show What Is In A Git Stash

Usually when I want to inspect anything in git, I’ll use git show with a specific ref. This can even be done with stash refs.

$ git stash list
stash@{0}: WIP on ...
stash@{1}: Some commit on ...

$ git show stash@{0}
# ...

The git-stash command has a built-in way of showing stashes that will save you from having to type out the somewhat awkward stash@{n} ref.

$ git stash show 1

This will show you the stash@{1} ref. You can also omit a number which will show you the latest stash (stash@{0}).

See man git-stash for more details.

Jump Back To The Latest Jump Position In Vim

Any sort of jump motion, such as gg or 121G, will be recorded in your Vim session’s jump list. If you’d like to return to the latest position in your jump list, you can tap '' within normal mode.

You can then tap '' again and you’ll be returned to the position you were just at. This is great if you want to toggle back and forth between your current position and the previous position.

If the latest jump position is not within the current buffer, then you will be jumped to the initial cursor position at the top of the file.

You can learn more about this by reference :help jump-motions, :help jumplist, and :help ''.

h/t Jake Worth

Compile ReasonML To Native With Dune

Dune is “a composable build system for OCaml” with out-of-the-box support for ReasonML. Dune can be used for a lot of things, but in simplest terms it can be used to compile ReasonML programs into native executables.

Considering the following ReasonML program.

/* hello_reason.re */
print_endline("Hello, Reason!")

We can then create the following Dune build file.

;; dune
(executable
 (name hello_reason))

If we then run dune build hello_reason.exe, then Dune will compile the hello_reason.re into a hello_reason.exe executable that can be found in build/default. Run it and see the output.

Read more in the Quickstart guide.

Doing Date Math In MySQL

MySQL has an array of functions for interacting with date and datetime values. If you’d like to do math with a date to compute a date in the future or the past, you can use the DATE_ADD() and DATE_SUB() functions.

mysql> select now() Now, date_add(now(), interval 10 minute) '10 Minutes Later';
+---------------------+---------------------+
| Now                 | 10 Minutes Later    |
+---------------------+---------------------+
| 2018-10-18 15:53:29 | 2018-10-18 16:03:29 |
+---------------------+---------------------+

mysql> select now() Now, date_sub(now(), interval 9 day) '9 Days Earlier';
+---------------------+---------------------+
| Now                 | 9 Days Earlier      |
+---------------------+---------------------+
| 2018-10-18 15:54:01 | 2018-10-09 15:54:01 |
+---------------------+---------------------+

There are equivalent ADDDATE() and SUBDATE() functions if you prefer.

Check out the docs for more details.

Get The Time Zone Of The Client Computer

The resolvedOptions function on Intl.DateTimeFormat.prototype provides a number of pieces of information about the client computer. It includes information such as the locale and the numbering system. It also has the time zone for that machine.

Try running this line of JavaScript in your own browser.

$ Intl.DateTimeFormat().resolvedOptions().timeZone

When I run it, I get America/Chicago.

You can use this within your client-side code as a way of determining in which time zone your users are.

Git: Stash Everything

Running the git stash command will take all the changes to tracked files in the working directory and stash them away. You can tack on the --include-untracked flag to make sure that untracked files are also included in the stash.

You can take this a step further with the --all flag. This will stash everything included files that you’ve told git to ignore.

You probably don’t want to do this. I ran this command and realized after the command hung for about 10 seconds that I had just stashed the node_modules directory.

See man git-stash for more details.

CSS !important Is Not Supported By Inline Styles

You can get pretty far with React’s inline styling of components. There are however a few limitations. One such limitation is that the !important clause is not supported.

If you try applying !important to an inline style like so:

<div style={{ color: "red !important" }}>
  My div
</div>

You’ll be disappointed when you open up the browser and inspect that div tag. The color rule will be ignored and excluded from the output html.

source

Show Git Changes For Files That Match A Pattern

The git show command allows you to view the changes associated with a reference, such as a commit sha (e.g. git show 86748aacf14e).

Consider a commit that has changed a bunch of JS files as well as two CSS files. If we run git show abcd1234, we will see all of the changes to each file which can result in quite a bit of noise. What if we only want to view the changes to the CSS files?

We can instruct the command to only show changes to files that match a pattern by tacking that pattern on to the end.

$ git show abcd1234 *.css

Alternatively, we could scope the output of the command to the files that live in a certain directory.

$ git show abcd1235 src/css

Wrap A ReasonReact Component For Use In JavaScript

Consider the following ReasonReact component for displaying a greeting.

let component = ReasonReact.statelessComponent("Hello");

let make = (~name, _children) => {
  ...component,
  render: _self =>
    <p> {ReasonReact.string("Hello, ")} {ReasonReact.string(name)} </p>,
};

If we will just be using this component in a ReasonML context, then that is all we need.

If we want this component available for use in a JavaScript file, we have a little more work to do. We need to define the shape of the component’s props using a bucklescript directive and then wrap the component so that it gets appropriate exported for a JavaScript context.

Here is what that looks like.

[@bs.deriving abstract]
type jsProps = {name: string};

let default =
  ReasonReact.wrapReasonForJs(~component, jsProps =>
    make(~name=jsProps->nameGet, [||])
  );

Our only prop is name which is a string. The wrapReasonForJs function and accompanying binding to default mean that we can import the compiled JS-equivalent like so:

import Hello from 'src/components/Hello.bs.js';

See the docs for more details on Reason/JS interop.

Format The Current ReasonML File Within Vim

I’m editing a .re file within Vim. I haven’t yet wired up refmt to something like ALE for automatic formatting on save. By the time I’m done with my changes, indentation is a mess.

I can still take advantage of refmt to clean up my file.

:!refmt --in-place %

Running that command in Vim will cause the current file to be formatted.

How does it work?

It shells-out to refmt which does all the heavy lifting. The --in-place flag means that the target file will be re-written by the formatted result. The % is a handy Vim shorthand for the path and name of the current file.

Animate Smoothly Between Two Background Colors

CSS animations allow you to set up simple rules that the rendering engine can then apply to create smooth transitions between style states.

To smoothly transition between two background colors, we can create a keyframes at-rule with a fitting name (e.g. pulse).

@keyframes pulse {
  0% {
    background-color: red;
  }
  50% {
    background-color: blue;
  }
  100% {
    background-color: red;
  }
}

Over the course of a single animation, this set of rules will start the background color at red, transition to blue 50% of the way through, and then back to red again.

We can then apply this animation within any of our CSS class definitions.

.square1 {
  animation: pulse 2s infinite;
  width: 100px;
  height: 100px;
}

Anything with a class of square1 will have a width and height of 100px as well as a pulsing background color.

Find The Process Using A Specific Port On Mac

The netstat utility is often recommended for finding the PID (process ID) bound to a specific port. Unfortunately, Mac’s version of netstat does not support the -p (process) flag. Instead, you’ll want to use the lsof utility.

$ sudo lsof -i tcp:4567

Running this will produce a nicely formatted response that tells you several pieces of information about the process bound to :4567 including the PID.

source

Enable Breadcrumbs For VS Code 1.26 Release

The latest release of Code (Version 1.26) brings about a lot of exciting new features — including file breadcrumbs at the top of each file editor view.

Breadcrumbs feature in action

By default this feature is not enabled. You can enable it from User Settings (Cmd+Shift+P, ‘User Settings’). From the User Settings view, you can search for breadcrumbs and you’ll see the following item:

  "breadcrumbs.enabled": false,

Use the pencil to override it to true and you’ll have that trail of breadcrumbs waiting for you.

Grep For Files With Multiple Matches

The grep utility is a great way to find files that contain a certain pattern:

$ grep -r ".class-name" src/css/

This will recursively look through all the files in your css directory to find matches of .class-name.

Often times these kinds of searches can turn up too many results and you’ll want to pare it back by providing some additional context.

For instance, we may only want results where @media only screen also appears, but on a different line. To do this, we need to chain a series of greps together.

$ grep -rl "@media only screen" src/css |
    xargs grep -l ".class-name"

This will produce a list of filenames (hence the -l flag) that contain both a line with @media only screen and a line with .class-name.

If you need to, chain more grep commands on to narrow things down even farther.

See man grep for more details.

Print The Relative Path Of The Current File

As a project grows and there are many directories and long path names, Vim will not always have enough room to display the full relative path of the current file. It may have to cut off the initial directories which can make it hard to know where you are.

You can hit Ctrl-g to quickly display the relative path of the current file below the status bar.

example of using ctrl-g

See :help Ctrl-g for more details.

Aborting Git Commits And Rebases Within Vim

When you are amending a commit or doing an interactive rebase of a series of commits, Vim will be open to a buffer full of content related to the respective action. Normally, you’ll make some changes, save the buffer, and then quit — Git will take over from there by processing the commit or rebase.

What if you find yourself in this situation and you want to cancel the commit or rebase? Simply quitting with text already in the buffer will be interpreted by Git as a signal to go ahead and process the commit/rebase.

So, how do we quit without confirming the action?

Vim allows you to quit with an error code.

:cq

This means that irrespective of the content of the buffer, Vim will signal to Git with an error code to not process the commit or rebase, effectively aborting the action.

See :help cq for more details.

Is This A Directory Or A File -- ReasonML

When compiling ReasonML natively, we have access to a variety of additional modules including the Unix module. We can interact with directories and files using functions on Unix.

let current_dir = Unix.opendir(Unix.getcwd());
let first_file = Unix.readdir(current_dir);
/* is first_file a directory or a file? */
Unix.closedir(current_dir);

Here we open the current working directory, grab the first thing out of that directory — maybe it’s a file, maybe it’s a directory, maybe it is something else. Lastly, we close the directory.

let current_dir = Unix.opendir(Unix.getcwd());
let first_file = Unix.readdir(current_dir);

switch(Unix.stat(first_file)) {
| Unix.stats({ st_kind: Unix.S_REG }) => print_endline("Regular File")
| Unix.stats({ st_kind: Unix.S_DIR }) => print_endline("Directory")
| Unix.stats({ st_kind: Unix.S_LINK }) => print_endline("Link")
| Unix.stats({ st_kind: Unix.S_SOCK }) => print_endline("Socket")
| _ => print_endline("Something else")
};

Unix.closedir(current_dir);

There are a variety of kinds of files to switch on. Here, we are switching on Regular Files, Directories, Links, and Sockets. Everything else falls through.

See the Unix module docs for more details.

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

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.

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

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

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] => [] */

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

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

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.