Today I Learned

A Hashrocket project

Syntactically correct OCaml AST at the cmd line

I wrote a til about a week ago where I used refmt to convert reasonml to AST but the output is this:

[
  structure_item ([1,0+0]..[1,0+19])
    Pstr_module
    "Something" ([1,0+7]..[1,0+16])
      module_expr ([1,0+17]..[1,0+19])
        Pmod_structure
        []
]

While this is a good clue, it’s not something I can copy and paste into my OCaml ppx file. It is not OCaml.

What’s better is using the dumpast tool that comes with ppx_tools (installable with opam install ppx_tools).

Given a reason file:

module Something {}

When I run:

> cat target.re | refmt --parse re --print ml > target.ml 
> ocamlfind ppx_tools/dumpast ./target.ml

It returns:

[{pstr_desc =
   Pstr_module
    {pmb_name = {txt = "Something"};
     pmb_expr = {pmod_desc = Pmod_structure []}}}]

Each of the records it returns is missing a location field and an attributes field, but you should use Ast_helper to produce Parseetree records with the location and attributes fields defaulted for you.

Match ocaml versions with Reasonml with `opam`

This week I was trying to write a ppx in ocaml use it in my reasonml project but I was getting an error about mismatching Ocaml versions.

Ast_mapper: OCaml version mismatch or malformed input

When I checked my versions sure enough there was a mismatch:

> bsc
BuckleScript 4.0.0 (Using OCaml4.02.3+BS )
> ocaml -version
The OCaml toplevel, version 4.07

Reasonml and Bucklescript use an older version of ocaml, version 4.02.3. I had compiled my ppx binary however with 4.07.

Fortunately it is rather easy to switch ocaml versions using opam, the Ocaml package manager. If you have opam installed (on mac brew install opam) then you can use the switch command.

opam switch 4.02.3

Running opam switch without an argument will show you all the available versions.

Custom Cypress Commands

I’ve been loving my first forays into Cypress integration testing. It feels really well made.

Today I learned how to create a custom Cypress command. Logging into the application is a classic use-case: I need to do it in almost every test, and I’d like a function that accepts arguments and centralizes the DOM manipulation.

Here it is:

// cypress/support/commands.js

Cypress.Commands.add('signIn', ({ email, password }) => {
  cy.visit('/sign-in');
  cy.get('input[name="email"]').type(email);
  cy.get('input[name="password"]').type(password);
  cy.get('form').submit();
});

The command is now available in any test without an explicit import:

cy.signIn({ email: 'dev@hashrocket.com', password: 'password' });

Abstraction!

docs

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.

Traversing Git Conflict Markers

Today I solved several nagging inefficiencies in my Vim setup. One was memorizing a mapping for traversing Git conflict markers.

If you’ve ever had a merge conflict and opened the unresolved file, you’ll see these markers:

<<<<<<<
console.log('keep this code?')
=======
console.log('...or this?')
>>>>>>>

Deciding what to keep can be a process, and Vim-Unimpaired makes it easier by providing mappings to jump between the markers— ]n for the next marker, [n for the previous marker. Use these to traverse the diff and learn about what might be gained or lost during resolution.

How to check Rails migration statuses

You can check the status of your migrations in Rails by running rails db:migrate:status

This command returns your database name, as well as a list of all your migrations, with their name and status

database: my-database-dev

Status | Migration ID | Migration Name
--------------------------------------
  up   | 201803131234 | Create users
  up   | 201803201234 | Create blogs
 down  | 201804031234 | Create posts

Convert Reasonml to Ast at the command line

I’m curious what module Something {} looks like after being translated to the Ocaml AST.

I can use the Reasonml tool refmt to generate the ast for me at the command line with the --print ast command line flag.

echo 'module Something {}' | refmt --parse re --print ast

Which outputs:

[
  structure_item ([1,0+0]..[1,0+19])
    Pstr_module
    "Something" ([1,0+7]..[1,0+16])
      module_expr ([1,0+17]..[1,0+19])
        Pmod_structure
        []
]

Copy files with progress in terminal w/rsync

When you need to transfer a lot of files from one location to another it’s sometimes useful to have some progress indication and maybe even a speed measure, or time remaining.

I recently had to transfer a few gigabytes of data from one computer to another. For this task I chose to use Rsync, since it is a command line utility that can preserve file metadata (permissions) and easily resume in case of an error.

Rsync ships with macOS by default, but if you want to get a more recent version, you can install it from homebrew.

There are two options for showing progress:

If you are transferring a few really big files you can use the --progress flag.

rsync -ah --progress source destination

This will list each file as it being transferred and show you the progress and speed in which the file is being transferred.

In my case I had a lot of small files so I chose to use --info=progress2.

rsync -ah --info=progress2 source destination

This will output something like this

2.26G  16%    6.13MB/s    0:05:51 (xfr#375313, to-chk=0/1165396)

Which represents the progress, speed and estimated time remaining for the entire transfer.

Assigning, Mutating, and Freezing a JS object

I’ve been using Object.assign to merge two objects in javascript. That typically looks like this:

> let o = Object.assign({a: 1, b: 2}, {a: 3})
{a: 3, b: 2}

The key/values in the second argument override the key/values in the first object to produce a new object with those the combined keys and values.

Does it actually produce a new object? or does it just mutate the first argument:

> foo = {a: 1, b: 2}
> bar = Object.assign(foo, {a: 3})
> foo == bar
true
> foo
{a: 3, b: 2}

Ahh… it mutates. If you prefer something not to mutate you can freeze it:

> foo = {a: 1}
> bar = Object.freeze(foo)
> Object.assign(bar, {a: 2})
TypeError: Cannot assign to read only property 'a' of object '#<Object>'

Ok, so now Object.assign can’t mutate the first argument, because that argument is frozen. But be careful, freeze mutates too.

> Object.assign(foo, {a: 2})
TypeError: Cannot assign to read only property 'a' of object '#<Object>'

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.

MySQL Average

MySQL; it’s like a calculator inside your computer. Today I learned how to compute the average of something using this popular RDBMS.

Let’s compute the average length of name in table dog.

mysql> select avg(char_length(name)) from dog;

Result:

+------------------------+
| avg(char_length(name)) |
+------------------------+
|                 9.2965 |
+------------------------+

Suppress warnings from bucklescript compiler

The bsconfig.json file has a configuration called bsc-flags that are sent to the bucklescript compiler (bsc). That looks like this by default:

"bsc-flags": ["-bs-super-errors"],

This are a host of flags you could send to the bsc compiler, and you can see them all with:

> bsc -warn-help
...
  6 Label omitted in function application.
  7 Method overridden.
  8 Partial match: missing cases in pattern-matching.
  9 Missing fields in a record pattern.
 10 Expression on the left-hand side of a sequence that doesn't have type
    "unit" (and that is not a function, see warning number 5).
...

Note the numbers that correspond with each warning. The bsc command takes a -w flag and you can explicitly tell the compiler to ignore a warning by using a minus sign in front of the warning number, like this:

"bsc-flags": ["-w -8", "-bs-super-errors"],

To ignore all warnings, use -w -A.

To turn all warnings into errors use -w @A.

Per the bsc -help the default setting is:

+a-4-6-7-9-27-29-32..39-41..42-44-45-48-50-102

Use a Lodash function in ReasonML w/Interop

This is my attempt to get into javascript interop in ReasonML. Really, you probably have enough tools in ReasonML to avoid using any Lodash functions but I’m using it to learn.

[@bs.module "lodash"] external myMin : array('a) => 'a = "min";

let result = myMin([|1, 2, 3|]);
/* result is 1 */

The bs.module syntax is not very well documented and a little obtuse.

[@bs.module "lodash"] will turn into the require statement in JS.

external myMin is where you name the function you’ll be using in Reason.

array('a) => 'a declares the type signature and return value.

= "min" is where we declare what the function is on the javascript side.

There are some parentheses missing that might help you read and parse this differently. Here is what the same statement looks like with added parentheses.

[@bs.module("lodash")] external myMin : (array('a) => 'a) = "min";

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.