<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dylan Irlbeck </title>
    <description>The latest articles on DEV Community by Dylan Irlbeck  (@dylanirlbeck).</description>
    <link>https://dev.to/dylanirlbeck</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F335792%2F569fe913-b521-4b27-adcb-1e641b453c1f.png</url>
      <title>DEV Community: Dylan Irlbeck </title>
      <link>https://dev.to/dylanirlbeck</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dylanirlbeck"/>
    <language>en</language>
    <item>
      <title>Kitty Sessions</title>
      <dc:creator>Dylan Irlbeck </dc:creator>
      <pubDate>Tue, 05 May 2020 16:36:54 +0000</pubDate>
      <link>https://dev.to/dylanirlbeck/kitty-sessions-44j2</link>
      <guid>https://dev.to/dylanirlbeck/kitty-sessions-44j2</guid>
      <description>&lt;p&gt;&lt;a href="https://sw.kovidgoyal.net/kitty/" rel="noopener noreferrer"&gt;Kitty&lt;/a&gt; is a super-fast, customizable, and GPU based terminal emulator. I recently switched over to Kitty from iTerm, and I could not be more satisfied. However, Kitty's speed is not the only great thing about it; in fact, Kitty has built-in support for tmux-like sessions and windows, allowing you to create powerful setups with no extra tools!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sessions
&lt;/h2&gt;

&lt;p&gt;One of the best features about Kitty are &lt;a href="https://sw.kovidgoyal.net/kitty/index.html#startup-sessions" rel="noopener noreferrer"&gt;startup sessions&lt;/a&gt;. Sessions allow you to create one or more tabs on startup (or with the &lt;code&gt;kitty --session&lt;/code&gt; command) and customize each tab with a unique terminal configuration. For example, my current &lt;code&gt;startup.conf&lt;/code&gt; looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;

&lt;span class="c"&gt;# startup.conf
&lt;/span&gt;&lt;span class="n"&gt;new_tab&lt;/span&gt; &lt;span class="n"&gt;tailwind_ppx&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt; ~/&lt;span class="n"&gt;Code&lt;/span&gt;/&lt;span class="n"&gt;reason&lt;/span&gt;/&lt;span class="n"&gt;tailwind_ppx&lt;/span&gt;
&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;
&lt;span class="n"&gt;launch&lt;/span&gt; &lt;span class="n"&gt;zsh&lt;/span&gt; -&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="s1"&gt;'nvim'&lt;/span&gt;
&lt;span class="n"&gt;launch&lt;/span&gt; &lt;span class="n"&gt;zsh&lt;/span&gt; -&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="s1"&gt;'esy watch'&lt;/span&gt;
&lt;span class="n"&gt;launch&lt;/span&gt; &lt;span class="n"&gt;zsh&lt;/span&gt;
&lt;span class="n"&gt;enabled_layouts&lt;/span&gt; &lt;span class="n"&gt;tall&lt;/span&gt;:&lt;span class="n"&gt;bias&lt;/span&gt;=&lt;span class="m"&gt;50&lt;/span&gt;;&lt;span class="n"&gt;full_size&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="n"&gt;tall&lt;/span&gt;

&lt;span class="n"&gt;new_tab&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt; ~
&lt;span class="n"&gt;launch&lt;/span&gt; &lt;span class="n"&gt;zsh&lt;/span&gt;
&lt;span class="n"&gt;enabled_layouts&lt;/span&gt; &lt;span class="n"&gt;tall&lt;/span&gt;:&lt;span class="n"&gt;bias&lt;/span&gt;=&lt;span class="m"&gt;50&lt;/span&gt;;&lt;span class="n"&gt;full_size&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="n"&gt;tall&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;which sets up two &lt;a href="https://sw.kovidgoyal.net/kitty/index.html#tabs-and-windows" rel="noopener noreferrer"&gt;tabs&lt;/a&gt; when I launch Kitty: the first is a Reason native project (in particular, &lt;a href="https://github.com/dylanirlbeck/tailwind-ppx" rel="noopener noreferrer"&gt;&lt;code&gt;tailwind-ppx&lt;/code&gt;&lt;/a&gt;) and the second is a new tab that I can use for whatever I need to. This configuration looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdylanirlbeck%2Ftil%2Fmaster%2Fassets%2Fkitty%2Fstartup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdylanirlbeck%2Ftil%2Fmaster%2Fassets%2Fkitty%2Fstartup.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  "Dynamic" Sessions
&lt;/h2&gt;

&lt;p&gt;Normal startup sessions are great if you've hard-coded the desired working directory for the session into the &lt;code&gt;.conf&lt;/code&gt; file, but what about when you want to start a session and you don't know the directory? With a little help from &lt;code&gt;zsh&lt;/code&gt;, we can implement what I'm calling "dynamic" Kitty sessions. Essentially, we're going to create a command that allows you to launch a new Kitty session given the path to a project. For example,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ kt-native ~/tailwind_ppx # Launch a Reason native session
$ kt-js ~/some_js_project # Launch a JavaScript session ```


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you can start to see, there's a lot of customization we can do in the middle, but hopefully you get the basics of what we're trying to do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining our session configurations
&lt;/h3&gt;

&lt;p&gt;Let's start by defining our session. Personally, &lt;a href="https://github.com/dylanirlbeck/dotfiles/tree/master/config/kitty" rel="noopener noreferrer"&gt;I've&lt;br&gt;
created&lt;/a&gt; one session file per type of project I work with (Reason native, BuckleScript, JavaScript, etc.), and it's worked well for me thus far.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that you can put your session files wherever you'd like, so long as you properly provide the path in your .zshrc (see the section on aliases below)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, here's my session for a Reason native project:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// ~/path/to/reason_native.conf

new_tab reason_native
cd ${PROJECT_DIR}
title vim launch zsh -c 'nvim'
launch zsh -c 'esy watch'
launch zsh
enabled_layouts tall:bias=50;full_size=1
layout tall


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Notice the &lt;code&gt;PROJECT_DIR&lt;/code&gt; environment variable above --- this variable will be set automatically before Kitty is launched, so stay tuned.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Creating launch aliases
&lt;/h3&gt;

&lt;p&gt;Now that we've created the session configuration files, we'll move on to creating shortcuts in &lt;code&gt;zsh&lt;/code&gt; so instead of doing this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ export PROJECT_DIR=/path/to/reason_native_project
$ kitty --session ~/path/to/reason_native.conf


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;we can just do&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ kt-native /path/to/reason_native_project


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Since these shortcuts will take in an argument (namely, the path to a project), all we need to do is add a function in our &lt;code&gt;.zshrc&lt;/code&gt; that will act as our alias.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// ~/path/to/.zshrc

# Kitty functions
function kt-native() {
  export PROJECT_DIR=$1
  kitty --session ~/path/to/reason_native.conf
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that's it! Now you can launch a new Kitty session based on the type of project, without hard-coding all the file paths of your projects into separate &lt;code&gt;.conf&lt;/code&gt; files beforehand.&lt;/p&gt;

&lt;p&gt;Hopefully this tutorial/intro was useful! If you're curious in learning more, I'd check out the &lt;a href="https://sw.kovidgoyal.net/kitty/#quickstart" rel="noopener noreferrer"&gt;Kitty docs&lt;/a&gt;. In addition, I've published my personal Kitty setup, along with the rest of my dotfiles, &lt;a href="https://github.com/dylanirlbeck/dotfiles" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;. Cheers!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Intro to PPXs, for Reason newcomers</title>
      <dc:creator>Dylan Irlbeck </dc:creator>
      <pubDate>Tue, 28 Apr 2020 21:52:43 +0000</pubDate>
      <link>https://dev.to/dylanirlbeck/intro-to-ppxs-for-reason-newcomers-2829</link>
      <guid>https://dev.to/dylanirlbeck/intro-to-ppxs-for-reason-newcomers-2829</guid>
      <description>&lt;p&gt;ReasonML (also known as Reason) has gotten very popular these days. It's a new syntax for OCaml that looks a lot like JavaScript, allowing you to develop with a rock-solid type system while still having strong interop with JavaScript and its strong ecosystem.&lt;/p&gt;

&lt;p&gt;That was a mouthful, I know. For a proper introduction (if you aren't familiar), I'd highly suggest reading through the &lt;a href="https://reasonml.github.io/docs/en/what-and-why"&gt;official Reason docs&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Now let's get into the point of this post - Reason PPXs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a PPX and Why Do I Care?
&lt;/h2&gt;

&lt;p&gt;A Pre-Processor eXtension (PPX) is a preprocessor that is applied to your code before it gets passed to the compiler. It allows for arbitrary manipulation of your code's Abstract Syntax Tree, rather than the code itself. &lt;/p&gt;

&lt;p&gt;For example, if you have ever written GraphQL queries in your Reason code, chances are you've used &lt;code&gt;[%graphql {|QUERY|}]&lt;/code&gt; to validate your queries - &lt;a href="https://github.com/reasonml-community/graphql_ppx"&gt;this is a PPX&lt;/a&gt;! In the case of &lt;code&gt;graphql_ppx&lt;/code&gt; , your query will be validated and is guaranteed to be type-safe. The query is then transformed into a normal string before your code is compiled.&lt;/p&gt;

&lt;p&gt;Another example is &lt;a href="https://github.com/dylanirlbeck/tailwind-ppx"&gt;&lt;code&gt;tailwind-ppx&lt;/code&gt;&lt;/a&gt;, a PPX I wrote to validate your Tailwind CSS classes at compile-time. Using this PPX, you can write your class names like &lt;code&gt;&amp;lt;Component className=[%tw "flex flex-row"] /&amp;gt;&lt;/code&gt; and get validation for zero extra cost (in case there's a typo in a class name or you included a class name twice, for example). &lt;/p&gt;

&lt;p&gt;PPXs are very powerful, but I found it rather difficult to get a barebones one up and running. Though I tried &lt;a href="https://github.com/jchavarri/hello-ppx-esy/"&gt;&lt;code&gt;hello-ppx-esy&lt;/code&gt;&lt;/a&gt; and eventually found some success, it seemed that the process should be friendly to both Reason newcomers and PPX newcomers, rather than just the latter. Namely, I felt that (1) Reason newcomers should not have to use &lt;a href="https://esy.sh"&gt;esy&lt;/a&gt;, (2) it should be more natural to run tests, and (3) publishing your PPX should be straightforward.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;hello-ppx-bucklescript&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/dylanirlbeck/hello-ppx-bucklescript"&gt;&lt;code&gt;hello-ppx-bucklescript&lt;/code&gt;&lt;/a&gt; is a small Github project I wrote a while back that provides the boilerplate for writing a Reason PPX. It takes care of configuring the BuckleScript compiler, hooking up tests, and generating your PPX executable. &lt;code&gt;hello-ppx-bucklescript&lt;/code&gt; was inspired by &lt;code&gt;hello-ppx-esy&lt;/code&gt;, but differs in that you do not need &lt;code&gt;esy&lt;/code&gt;, running tests is simple with &lt;code&gt;yarn test&lt;/code&gt;, and it includes a publish folder for making your PPX immediately available on the NPM registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project structure
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;publish/&lt;/code&gt; - basic setup for publishing your PPX to the NPM registry.&lt;br&gt;
&lt;code&gt;src/&lt;/code&gt; - contains the source code for the PPX.&lt;br&gt;
&lt;code&gt;tests/&lt;/code&gt; - a single test file that can be run with &lt;code&gt;yarn test&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  PPX source code
&lt;/h2&gt;

&lt;p&gt;The ppx included in the project essentially turns the &lt;code&gt;[%hello]&lt;/code&gt; expression into the integer literal &lt;code&gt;42&lt;/code&gt; at compile-time. The code to do this operation is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;open Ast_mapper;
open Parsetree;
let expr = (mapper, e) =&amp;gt;  
   switch (e.pexp_desc) {
   /* If the expression is [%hello] */  
   | Pexp_extension(({txt: "hello", loc, _}, _payload)) =&amp;gt;    
     /* Then replace by 42 */
     Ast_helper.Exp.constant(Asttypes.Const_int(42))  
   | _ =&amp;gt; default_mapper.expr(mapper, e)  
   }; 
let mapper = _ =&amp;gt; {...default_mapper, expr}; 
let () = run_main(mapper);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's walk through each part.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;open Ast_mapper;
open Parsetree;
Use the open directive to make the Ast_mapper and Parsetree modules directly accessible by the Hello module. See here for more information on modules.
let expr = (mapper, e) =&amp;gt;  
   switch (e.pexp_desc) {
   /* If the expression is [%hello] */  
   | Pexp_extension(({txt: "hello"}, _)) =&amp;gt;    
     /* Then replace by 42 */
     Ast_helper.Exp.constant(Asttypes.Const_int(42))  
   | _ =&amp;gt; default_mapper.expr(mapper, e)  
   };
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;expr is a function that takes in a mapper and an expression and returns a new expression. For the &lt;code&gt;[%hello]&lt;/code&gt; PPX, &lt;code&gt;expr&lt;/code&gt; is the function that we care about most.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch (e.pexp_desc) {
   ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here we're going to switch on the value of the &lt;code&gt;pexp_desc&lt;/code&gt; property of the input expression. &lt;code&gt;pexp_desc&lt;/code&gt; is of type &lt;code&gt;expression_desc&lt;/code&gt;, and thus can have many different constructors (see its type &lt;a href="https://caml.inria.fr/pub/docs/manual-ocaml/compilerlibref/Parsetree.html#TYPEexpression_desc"&gt;here&lt;/a&gt;) - however, all we really care about is if it's a PPX extension (&lt;code&gt;Pexp_extension&lt;/code&gt;) and the extension name is &lt;code&gt;hello&lt;/code&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Pexp_extension(({txt: "hello", _}, _)) =&amp;gt;
   ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we fall into this case our expression matches the &lt;code&gt;Pexp_extension&lt;/code&gt; constructor, and as a result, we need to modify the AST! We can ignore the other properties of a &lt;code&gt;Pexp_extension&lt;/code&gt;, since all we care about is that the &lt;code&gt;txt&lt;/code&gt; (the extension name) is &lt;code&gt;hello&lt;/code&gt; and not something like &lt;code&gt;graphql&lt;/code&gt; or &lt;code&gt;tw&lt;/code&gt;. We'll replace the AST with the integer literal &lt;code&gt;42&lt;/code&gt;, and then we're done!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Pexp_extension(({txt: "hello", loc, _}, _payload)) =&amp;gt;
  /* Then replace by 42 */
  Ast_helper.Exp.constant(Asttypes.Const_int(42))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That's it! The rest of the code is just PPX boilerplate that doesn't need much conceptual understanding. Feel free to ask questions below if you're confused, though!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;I could not have written this article, nor created &lt;code&gt;hello-ppx-bucklescript&lt;/code&gt; or &lt;code&gt;tailwind-ppx&lt;/code&gt;, without the following &lt;code&gt;[%incredible]&lt;/code&gt; sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.hackages.io/reasonml-ppx-8ecd663d5640"&gt;https://blog.hackages.io/reasonml-ppx-8ecd663d5640&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jchavarri/hello-ppx-esy/"&gt;https://github.com/jchavarri/hello-ppx-esy/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tarides.com/blog/2019-05-09-an-introduction-to-ocaml-ppx-ecosystem"&gt;https://tarides.com/blog/2019-05-09-an-introduction-to-ocaml-ppx-ecosystem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading! And please check out &lt;a href="https://github.com/dylanirlbeck/tailwind-ppx"&gt;&lt;code&gt;tailwind-ppx&lt;/code&gt;&lt;/a&gt; if you're thinking about building a new ReasonReact + TailwindCSS project!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
