<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>pes18fan&apos;s posts</title><description>Posts about random stuff, I guess.</description><link>https://pes18fan.github.io/</link><item><title>achieving true zen pt. 1: everything is an expression</title><link>https://pes18fan.github.io/posts/achieving_true_zen_pt1/</link><guid isPermaLink="true">https://pes18fan.github.io/posts/achieving_true_zen_pt1/</guid><description>How do you interpret an imperative language as only expressions?</description><pubDate>Fri, 29 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This article is part of a series.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In 2023, I read this little book called &lt;a href=&quot;https://craftinginterpreters.com&quot;&gt;Crafting Interpreters&lt;/a&gt;
written by Bob Nystrom, intrigued by the concept of PL design and compilers.
As the book intended, I worked parallely on writing the two interpreters designed 
in the book over the course of reading it. The first one, &lt;code&gt;jlox&lt;/code&gt;, I designed
as a Crystal interpreter named &lt;code&gt;kaze&lt;/code&gt;. I&apos;ve talked about in a previous blog,
where I described &lt;a href=&quot;/posts/no_semicolons&quot;&gt;how one could design a language to avoid semicolon terminators&lt;/a&gt;.
But the second interpreter, &lt;code&gt;clox&lt;/code&gt;, is where it got interesting for me. My
implementation was now written in Odin, named &lt;a href=&quot;https://github.com/pes18fan/zen&quot;&gt;&lt;code&gt;zen&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Initially, it was just another form of &lt;code&gt;clox&lt;/code&gt; with some small design differences,
most notably the avoidance of semicolons, albeit in a different way as was
described in my article, as I used automatic semicolon insertion (ASI) for it.
However as the years passed, I just kept adding more and more features that
were not in the toy language &lt;code&gt;clox&lt;/code&gt;, like anonymous functions, lists, a featureful
standard library, and proper modules, making it suspiciously like an actually
useful programming language. This year, I finally shed the single-pass nature
of &lt;code&gt;clox&lt;/code&gt; for a real abstract syntax tree (AST); unlocking so much more
possibilities for the language.&lt;/p&gt;
&lt;p&gt;I now have decided on four concrete steps on how I can &quot;achieve true zen&quot;.
The first one is: making everything an expression.&lt;/p&gt;
&lt;h1&gt;But why?&lt;/h1&gt;
&lt;p&gt;There are two main reasons behind it.&lt;/p&gt;
&lt;p&gt;Firstly, I think that it&apos;s just a really elegant design. Isn&apos;t something like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var something = if cond {
    thing
} else {
    fallback
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;a much cleaner design than the following?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var something = thing
if not cond {
    something = fallback
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What if you forget that if check? Your logic has now become entirely flawed.
The &lt;code&gt;if&lt;/code&gt; being an expression also makes it far more ergonomic to read as its
just &lt;code&gt;something is value if cond and fallback otherwise&lt;/code&gt;, compared to the
&lt;code&gt;something is value, and if cond is not true put fallback in it&lt;/code&gt;. It just feels
much more natural. Making statements like &lt;code&gt;switch&lt;/code&gt; an expression can
extend this idea even further.&lt;/p&gt;
&lt;p&gt;The second reason, which is perhaps more important technically, is more specific
to my plans, though. Before I even thought of doing this as a concrete idea, 
I was spending time teaching myself &lt;a href=&quot;https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system&quot;&gt;the Hindley-Milner type system&lt;/a&gt;,
in order to implement it in zen and make it statically typed. However, once
I finally set out to try and actually do it, I realized that it does not
work very well for traditional imperative-type languages whose grammar separates
expressions and statements, which zen was at that time.&lt;/p&gt;
&lt;p&gt;Why? &lt;/p&gt;
&lt;p&gt;To understand why, one needs to understand what Hindley-Milner originally was
for. The basic Hindley-Milner type system is implemented for the
lambda calculus, which can be considered as this extremely minimal &quot;programming
language&quot;; essentially the first ever functional language, although it was not
a programming language in the sense that we know it today but rather a method
to represent computation, similar to a Turing machine. The lambda calculus
has an exceedingly simple grammar with just expressions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(* an expression can be: *)
e ::= x                 (* a variable *)
    | λx.e              (* a function with parameter x and body e *)
    | e e               (* a function (first e) called with an argument (second e) *)
    | let x = e in e    (* a let-in expression; binds an expression for use in another *)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because HM&apos;s base is the lambda calculus, it works naturally with languages 
which have the functional idea of using expressions to denote everything. Run 
on an AST, it expects everything to be an expression that gives you a value of 
a certain type. Statements and declarations, which do not evaluate to anything 
break that idea; so you need to either special-case them which is annoying, or 
flatten the entire AST so that everything yields a value.&lt;/p&gt;
&lt;p&gt;My choice among the two was the latter: just turn everything into an expression
so that it works perfectly with HM!&lt;/p&gt;
&lt;p&gt;zen has always had a bit of a functional touch. Instead of opting for
something like uniform function call syntax or leaning into method chaining,
I decided to borrow the pipe operator &lt;code&gt;|&amp;gt;&lt;/code&gt; from Elixir instead. While Crafting
Interpreters did give zen first-class functions, what it did not give it was 
anonymous functions as well as shorthand arrow functions. Therefore, this felt
like a step in the right direction.&lt;/p&gt;
&lt;h1&gt;How exactly is it done?&lt;/h1&gt;
&lt;p&gt;The main idea is: you reinterpret every single AST node as something that returns
a value. This means that a dichotomy of statements and expressions, or in zen&apos;s
case a trichotomy with declarations, statements and expressions, flattens out
into just expressions. How do you do this for each type of node?&lt;/p&gt;
&lt;h2&gt;Sequence expressions&lt;/h2&gt;
&lt;p&gt;The very first thing that is necessary to clear up is: how do you describe
a program? If the program is only composed of expressions, does that mean
the program is itself an expression? How does that work? How do you interpret 
all of the separate expressions that the program contains as that one big
expression?&lt;/p&gt;
&lt;p&gt;The answer to that is &lt;strong&gt;sequence expressions&lt;/strong&gt;. I modeled a sequence expression
as two expressions, separated by a specific separator token (in my case a
newline or a semicolon). The grammar is thus like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sequence ::= expr (&quot;\n&quot; | &quot;;&quot;) expr?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As it is an expression, it has to evaluate to something. How does it evaluate?
Well, it evaluates the left expression, then &lt;strong&gt;discards&lt;/strong&gt; that result, evaluates
the right expression, and returns the value of that right expression as its value.
The right expression is optional; if it doesn&apos;t exist the sequence evaluates
to a null value. With this logic, something like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1; 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;naturally evaluates to the value 2, disregarding the evaluation of the previous
expression &lt;code&gt;1&lt;/code&gt;. This is very similar to how Rust does things; almost everything
in Rust is an expression as well, and the semicolon acts as a &quot;discard&quot; operator;
resembling how in my model of the sequence a newline without a right expression
evaluates to a null value as it has already &quot;discarded&quot; the previous expression.&lt;/p&gt;
&lt;p&gt;There are a few really useful benefits to this approach. Firstly, it perfectly
models normal statement-based code while still being a valid expression. You
gotta make sure that the language can still feel familiar and &quot;normal&quot; even if 
it is different underneath, otherwise it will just be a nightmare to understand.&lt;/p&gt;
&lt;p&gt;Secondly, it allows us to do really cool things like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var a = {
    var b = 3
    b * 2
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can describe a variable as a chain of expressions. How cool is that?&lt;/p&gt;
&lt;p&gt;But... how exactly do you model it in the parser (in this case, a Pratt parser)?
Do you make the newline an  infix operator with a defined precedence level in 
the parser? The answer is... sort of. Yes, the newline does indeed act as an 
infix operator, and it does have a precedence; it has the lowest possible precedence
as it is the chainer of EVERY possible expression. However, it is not ideal to 
just slap it on the precedence list for the parser, as it is not something that 
should just be able to appear anywhere within a program. The better approach is 
to define two levels of expression-parsing function. In my parser, it looks
something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;parse :: proc(p: ^Parser) -&amp;gt; (expr: Expr, success: bool) {
    if parser_is_at_end(p) {
        return nil, true
    }
    return parse_expression_top(p), !p.had_error
}

parse_expression_top :: proc(p: ^Parser) -&amp;gt; Expr {
    fst := parse_expression(p)
    if !parser_match(p, .NEWLINE, .SEMI) {
        return fst
    }

    if p.panic_mode {
        parser_synchronize(p)
    }

    seq := new(SequenceExpr)
    seq.token = parser_previous(p)
    seq.left = fst
    seq.operator = parser_previous(p)
    if parser_is_at_end(p) || parser_check(p, .RSQUIRLY) {
        seq.right = nil
    } else {
        seq.right = parse_expression_top(p)
    }
    return seq
}

parse_expression :: proc(p: ^Parser) -&amp;gt; Expr {
    // ... Pratt parsing logic ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You parse the first expression using the existing expression parsing logic.
Then, you check if there actually is a newline or semicolon afterward, and if
not, just return. Remember, your program is entirely allowed to be just a 
single random expression (even though that&apos;s never really useful). If you DO see
a newline, we actually have an actual sequence, and we look for the second
operand.&lt;/p&gt;
&lt;p&gt;But you have to be careful here as well. What if the program just goes to EOF
immediately after (or if we&apos;re at the end of a block; we&apos;ll get to that later)?
In that case the second operand doesn&apos;t exist, so you just return the one-armed
sequence. If it DOES exist, you parse it as well.&lt;/p&gt;
&lt;p&gt;Note how the left one just parses an expression, but the right one recursively
calls &lt;code&gt;parse_expression_top&lt;/code&gt;. This is to allow that sequence of expressions
to actually be a sequence; something like &lt;code&gt;1; 2; 3; 4&lt;/code&gt; gets parsed as
&lt;code&gt;(seq 1 (seq 2 (seq 3 4)))&lt;/code&gt;, allowing the entire program to just be one
huge expression. That is just immensely satisfying.&lt;/p&gt;
&lt;p&gt;As I stated earlier, while the parser is a Pratt parser with every
operator having its own separate parsing rule, this one specific part of
the parser is not a part of that logic; the newline is not given a Pratt
infix rule. Why?&lt;/p&gt;
&lt;p&gt;This is because the sequence expression must appear &lt;strong&gt;only in specific cases&lt;/strong&gt;.
Specifically, the sequence expression must appear in the top level (global scope)
as the single expression connecting all global expressions, and inside blocks
connecting the sequential operations. The sequence expression should not be
usable as just another general-purpose expression. Why? Well, it was mainly
just a design decision; it doesn&apos;t really make any sense to use a sequence
expression outside these two contexts.&lt;/p&gt;
&lt;h2&gt;if/switch&lt;/h2&gt;
&lt;p&gt;The branching statements; these always tend to be the ones appreciated a
lot when given expression forms. This is because they just are so useful
and intuitive in that form.&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;if&lt;/code&gt; expression, the idea was pretty simple; it evaluates to whatever
value the matching branch returns.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;val hello = if happy { &quot;hey!&quot; } else { &quot;hi.&quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The more interesting case is when there is no &lt;code&gt;else&lt;/code&gt; branch at all. What
do you evaluate to? The obvious idea would be &quot;evaluate to the &lt;code&gt;if&lt;/code&gt; branch if
that matches, otherwise return a nil value&quot;. But that&apos;s rather unpredictable; you
want your expressions to give you predictable values as far as possible. One
approach you could take is just disallow one-armed &lt;code&gt;if&lt;/code&gt; when it is used as a value
entirely. This means&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;val something = if cond { &quot;hi&quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;would crash with a compile error. This is what Rust does, and it is reasonable;
it doesn&apos;t really make sense to assign the value of a condition without any
fallback. But I felt that this was too restrictive, plus finding out when
a value is &quot;being used&quot; is actually rather annoying; there&apos;s a lot of cases!
So in the end, I decided the best approach would be to just always return &lt;code&gt;nil&lt;/code&gt;
for one-armed &lt;code&gt;if&lt;/code&gt;. The one-armed &lt;code&gt;if&lt;/code&gt; cannot really be considered as a proper
&quot;expression&quot;, but more of an effect. So, in the previous code example,
&lt;code&gt;something&lt;/code&gt; would evaluate to &lt;code&gt;nil&lt;/code&gt;, even if &lt;code&gt;cond&lt;/code&gt; was true.&lt;/p&gt;
&lt;p&gt;This approach can be annoying if the user expects the &lt;code&gt;if&lt;/code&gt; to return some value
when the condition is true only; but when the typechecker is here, it can enforce
the rule to disallow you from using the resultant &lt;code&gt;nil&lt;/code&gt; of a one-armed &lt;code&gt;if&lt;/code&gt; in
contexts that it doesn&apos;t make sense in.&lt;/p&gt;
&lt;p&gt;The switch statement is pretty simple. Whatever case matches, the switch
statement evaluates to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// `switch` evaluates to whatever case `day`&apos;s matching value is associated with
val message = switch day {
    &quot;friday&quot; =&amp;gt; &quot;yay weekend!&quot;
    &quot;saturday&quot; =&amp;gt; &quot;still weekend!&quot;
    else =&amp;gt; &quot;ugh&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The switch statement is a little stricter than the &lt;code&gt;if&lt;/code&gt;. Switch statements are
exhaustive in zen, albeit naïvely, means the compiler doesn&apos;t actually know if
you covered all the cases of a switch, it just always wants an &lt;code&gt;else&lt;/code&gt; fallback. 
This still does make the &lt;code&gt;switch&lt;/code&gt; far more predictable though; it ALWAYS returns 
a valid value and not some sentinel like &lt;code&gt;nil&lt;/code&gt; (unless you explicitly returned that).&lt;/p&gt;
&lt;h3&gt;about &lt;code&gt;nil&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;One thing I&apos;d like to clear up right now is my idea of &lt;code&gt;nil&lt;/code&gt; in zen. In many
languages, &lt;code&gt;nil&lt;/code&gt; is something that can implicitly coerce to any type and thus
a valid value of any type. Technically, zen does do that right now, but that
is simply the consequence of the fact that it is a dynamic language. &lt;code&gt;nil&lt;/code&gt;
is intended as the unit type: similar to &lt;code&gt;()&lt;/code&gt; in Rust, where it has its own
type where &lt;code&gt;nil&lt;/code&gt; is the only valid value, and you can&apos;t set something of
another type to &lt;code&gt;nil&lt;/code&gt;. This makes it a perfect candidate to describe effect-y
expressions like the one-armed if. In the future I&apos;ll probably turn it into
a type like &lt;code&gt;()&lt;/code&gt; anyways, but that is when I have proper record/tuple types
in the language.&lt;/p&gt;
&lt;h2&gt;blocks&lt;/h2&gt;
&lt;p&gt;This one is pretty cool! The expression basis now allows you to do this like
in Rust:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var a = {
    var b = 6
    var c = 7
    b * c
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As described in the part about sequence expressions, blocks are the only place
other than the global scope which can contain sequence expressions (or any other
expression really, as &lt;code&gt;parse_expression_top()&lt;/code&gt; will just return a non-sequence
expression if there is no sequence). Hence, the block doesn&apos;t really have
any logic of its own; it can be considered as a way to enclose sequence
expressions to make them usable as values. Blocks evaluate to whatever
expression is inside of them; which means visually they appear to evaluate
to whatever is the last expression, as sequence expressions do exactly that, 
and other expressions in a block will always be the &quot;last expression&quot; in the 
block anyways.&lt;/p&gt;
&lt;p&gt;However, they also add something really important to the base concept of the 
sequence expression; scoping. As always, the variables inside a block cannot
be used anywhere outside, making them useful for adding little isolated
computation blocks.&lt;/p&gt;
&lt;p&gt;One issue I faced when making blocks expressions was something I like to call
&quot;return value erasure&quot;. Every time a block goes out of scope, it pops off all of
its local variables from the stack. Consider that small example from before.
Right as that block&apos;s execution is done, the VM&apos;s value stack looks something
like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[ b ] [ c ] [ b * c ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;m massively oversimplifying of course, there&apos;s some other things in there;
but for now just consider those three values; the &lt;code&gt;b&lt;/code&gt; and &lt;code&gt;c&lt;/code&gt; being the local 
variables declared in the block living on the stack, and the &lt;code&gt;b * c&lt;/code&gt; being the
intended return value of the block.&lt;/p&gt;
&lt;p&gt;The block&apos;s scope now ends. In the old zen, by the time a block&apos;s scope
ended, everything coming from computations in the scope was not on the
stack anymore, and the only things left were its local variables; so the
VM would just blindly pop as many values off the stack as there were local
variables in the block, and it was safe.&lt;/p&gt;
&lt;p&gt;But now, the return value is on top of the stack, and obviously it can&apos;t be
just thrown off to make way for the locals to be cleared, because we need it in
the context after the stack ends. To fix this issue, I added a new field, or
should I call &quot;register&quot;, to my &lt;code&gt;VM&lt;/code&gt; struct called &lt;code&gt;save&lt;/code&gt;. It has this hyper-specific
purpose: guard a block&apos;s return value as its scope is being torn down.&lt;/p&gt;
&lt;p&gt;So, right before the block scope ending causes the block scope to be cleared,
an &lt;code&gt;OP_SET_SAVE&lt;/code&gt; instruction grabs the top of the stack and puts it in the
&lt;code&gt;save&lt;/code&gt; register, thus turning the stack just into&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[ b ] [ c ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The block can now safely pop off these local variables, emptying the stack.
Then the value inside &lt;code&gt;save&lt;/code&gt; is immediately put back on the stack, getting&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[ b * c ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Solving the whole issue with ease!&lt;/p&gt;
&lt;p&gt;The one unfortunate result of this is that it is an additional two cycles on
the VM, which has some real performance implications. It definitely feels like
a bit of a temporary fix than anything else; hence I&apos;m still thinking of
more optimized approaches to this idea, perhaps like a dedicated block scope
ending opcode.&lt;/p&gt;
&lt;h2&gt;while/for/for-in&lt;/h2&gt;
&lt;p&gt;I thought about what would be a logical thing for a loop to evaluate to. Then
I realized; there really isn&apos;t anything that makes sense. Some accumulated
value? Not all loops accumulate values. The index? Not all loops care about
the index. Anything else is too weird of a choice.&lt;/p&gt;
&lt;p&gt;Except one that almost made sense: returning a value via a &lt;code&gt;break&lt;/code&gt;. Rust actually
does this for its &lt;code&gt;loop&lt;/code&gt; expressions. However, I realized that doesn&apos;t actually
make sense for the &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt; and &lt;code&gt;for-in&lt;/code&gt; loops that zen has at all.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;loop&lt;/code&gt; statement in Rust can actually be represented with a concrete
type, which would be whatever type of the value the &lt;code&gt;break&lt;/code&gt; has inside it,
and in all other cases it just never returns so it gets the type of the
diverging expression, which in Rust is called &lt;code&gt;!&lt;/code&gt;. The latter is also called
the &lt;code&gt;never&lt;/code&gt; type or the bottom type, more on it later.&lt;/p&gt;
&lt;p&gt;So in the end, I decided that all loops should just evaluate to &lt;code&gt;nil&lt;/code&gt;; that
is the only idea that makes sense; not everything has a natural value it
evaluates to. It is a bit unfortunate but that&apos;s just how it is, loops are
not values, they&apos;re effects.&lt;/p&gt;
&lt;h2&gt;var/val&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;var&lt;/code&gt; and &lt;code&gt;val&lt;/code&gt; declarations return &lt;code&gt;nil&lt;/code&gt;. I considered making them
return what they bound; but decided that didn&apos;t really make a lot of sense
since again, they&apos;re more like side effects rather than something that
directly returns some sensible value. This declaration is kind of like the
&lt;code&gt;let x = e&lt;/code&gt; part of the &lt;code&gt;let-in&lt;/code&gt; lambda calculus expression; it doesn&apos;t make
sense to break that apart and get a value-returning expression does it? Same
logic.&lt;/p&gt;
&lt;h2&gt;func&lt;/h2&gt;
&lt;p&gt;This was something I did a little trick for. For variable declarations, I had
taken the &lt;code&gt;VarDecl&lt;/code&gt; declaration node and turned it into a &lt;code&gt;VarDeclExpr&lt;/code&gt; that
evaluates to &lt;code&gt;nil&lt;/code&gt;. I could&apos;ve done the same thing for the &lt;code&gt;FuncDecl&lt;/code&gt; since
its the same concept... but then I realized... yep, its the same concept; hence
they can just be merged into one. The function declarations &lt;code&gt;func name() {}&lt;/code&gt;
are now parsed as &lt;code&gt;VarDeclExpr&lt;/code&gt;s that bind an anonymous function to a name.
This means that function declarations are just syntactic sugar for a variable
declaration binding a lambda.&lt;/p&gt;
&lt;p&gt;This was a very seamless and satisfying merger that only needed minimal adjustments
to make sure function name printing worked (you wouldn&apos;t want your named function
to be printed as &lt;code&gt;&amp;lt;func lambda&amp;gt;&lt;/code&gt; now would you?)&lt;/p&gt;
&lt;h2&gt;print&lt;/h2&gt;
&lt;p&gt;I thought about exactly how to go about this for a while. The actual implementation
is trivial, so that was not the worry; the worry was: do I really need this?
I could just get rid of this and create a native function for printing instead,
just like how &lt;code&gt;puts&lt;/code&gt; exists now. But I decided to keep it anyways. It felt like
some sort of &quot;heritage&quot; I inherited from clox.&lt;/p&gt;
&lt;p&gt;One thing I did with this that some might find interesting is its return value.
You&apos;d expect a printing expression or function to usually return &lt;code&gt;nil&lt;/code&gt;; that
is what they tend to do. But, the &lt;code&gt;print&lt;/code&gt; expression actually returns what
it printed instead. This was a choice directly inspired from &lt;a href=&quot;https://nushell.sh&quot;&gt;nushell&lt;/a&gt;.
Honestly I&apos;m not fully sure if its the best of choices, and I may revisit it 
later, but for now, its there.&lt;/p&gt;
&lt;h2&gt;break/continue/return/exit&lt;/h2&gt;
&lt;p&gt;These are the juicy ones. What the hell do these even return?&lt;/p&gt;
&lt;p&gt;The answer is: &lt;strong&gt;literally nothing&lt;/strong&gt;. But you might ask, &quot;hey, the loops and
variable declarations didn&apos;t return anything either!&quot; And you would be...
somewhat correct; yes, logically they&apos;re just effects but there is no harm
or weirdness in making them return some unit value &lt;code&gt;nil&lt;/code&gt; anyways. This is
not the case for these four expressions.&lt;/p&gt;
&lt;p&gt;Take this expression:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var a = exit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is the value of &lt;code&gt;a&lt;/code&gt;? Nothing, because the entire program exited before that
binding could even be processed!&lt;/p&gt;
&lt;p&gt;Therefore, these four are the only expressions that don&apos;t actually leave any
tangible value on the stack, because the control flow never gets there:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;continue&lt;/code&gt; quits the entire iteration before its context is evaluted&lt;/li&gt;
&lt;li&gt;&lt;code&gt;break&lt;/code&gt; quits the entire loop&lt;/li&gt;
&lt;li&gt;&lt;code&gt;return&lt;/code&gt; quits the entire function&lt;/li&gt;
&lt;li&gt;and &lt;code&gt;exit&lt;/code&gt; quits the entire program!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might be thinking, &quot;Well, this sounds like a nightmare to represent with
types? What the hell is the type of an &lt;code&gt;exit&lt;/code&gt; expression?&quot; In fact, it is
actually trivial to represent these expressions as types. They are a classic
case of the &lt;code&gt;never&lt;/code&gt; or &lt;code&gt;!&lt;/code&gt; type: the type of a &quot;diverging&quot; expression that never
returns to its origin of evaluation. It can also be thought of as the type of
a value that can never exist, or a type which no value ever has.&lt;/p&gt;
&lt;p&gt;Functions like &lt;code&gt;panic&lt;/code&gt; which are used to instantly crash a program also are
associated with this type; they are treated as functions which return a value of
type &lt;code&gt;never&lt;/code&gt;; basically saying &quot;this function doesn&apos;t just return nothing, it
NEVER returns in the first place&quot;.&lt;/p&gt;
&lt;p&gt;In type theory, &lt;code&gt;never&lt;/code&gt; is what we call a &quot;bottom type&quot;; a type that is compatible 
with every other type. It is why a function like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func f() {
    if cond {
        return value
    }

    panic(&quot;some error&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;has a return type which is the return type of &lt;code&gt;value&lt;/code&gt;; the &lt;code&gt;never&lt;/code&gt; type of the
&lt;code&gt;panic&lt;/code&gt; call at the end of the function happily fits with the type of &lt;code&gt;value&lt;/code&gt;,
because if &lt;code&gt;cond&lt;/code&gt; is satisfied you&apos;ll get something with the type of &lt;code&gt;value&lt;/code&gt;
and if not the whole program will crash anyways!&lt;/p&gt;
&lt;h2&gt;discard&lt;/h2&gt;
&lt;p&gt;One annoying consequence of everything being an expression is unintentional
return values. Consider a function like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func f() {
    some_numeric_computation()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where &lt;code&gt;some_numeric_computation()&lt;/code&gt; does that particular numeric computation and
returns some value. &lt;code&gt;f()&lt;/code&gt; is just meant to be an abstraction for that computation
action rather than actively returning a value. However, as &lt;code&gt;some_numeric_computation()&lt;/code&gt;
returns a value and it is at the end of its block, &lt;code&gt;f()&lt;/code&gt; will also unintentionally
return that value, because blocks evaluate to whatever is at the end of them.
That is not what we wanted. This is where the newly added &lt;code&gt;discard&lt;/code&gt; operator 
comes in.&lt;/p&gt;
&lt;p&gt;Now consider that same function modified as&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func f() {
    discard some_numeric_computation()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;discard&lt;/code&gt; is a very simple concept; it is an expression that takes any other
expression within it, runs whatever is in that expression, and as it says,
throws that value away. The discarded value is replaced with &lt;code&gt;nil&lt;/code&gt;. Hence,
&lt;code&gt;f()&lt;/code&gt; now returns &lt;code&gt;nil&lt;/code&gt; rather than whatever &lt;code&gt;some_numeric_computation()&lt;/code&gt;
returned.&lt;/p&gt;
&lt;p&gt;The choice of making the discard operation a keyword was inspired by &lt;a href=&quot;https://nim-lang.org&quot;&gt;Nim&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Things that don&apos;t make sense now&lt;/h1&gt;
&lt;p&gt;In the statement realm, one pervasive concept is the &lt;strong&gt;expression statement&lt;/strong&gt;,
which is basically just an expression wrapped around in statement clothing,
allowing things like function calls which are naturally expressions to live
in the statement world and be parsed as a step in that set of steps. But now
that the sequence expression handles parsing expressions as a set of steps without
any need to wrap them in anything, this is a concept that just doesn&apos;t make sense
and can safely disappear.&lt;/p&gt;
&lt;p&gt;Another thing that disappeared was this thing called the &lt;code&gt;EmptyStmt&lt;/code&gt;. It was
a concept somewhat similar to Python&apos;s &lt;code&gt;pass&lt;/code&gt;, which is basically just a
statement that doesn&apos;t do anything, emitted when a &lt;code&gt;;&lt;/code&gt; is parsed in statement
position. It was a bit spurious and now disappears; a &lt;code&gt;;&lt;/code&gt; appearing randomly
in expression position is now a parse error.&lt;/p&gt;
&lt;h1&gt;Some things I traded off&lt;/h1&gt;
&lt;p&gt;The expression model is not really without its faults. I have touched on some
of them beforehand; but I&apos;d like to expand on those a bit.&lt;/p&gt;
&lt;p&gt;The first and more obvious one is the spurious values returned by the expressions
that really aren&apos;t natural expressions. Loops don&apos;t return values. Variable
declarations don&apos;t evaluate to something. The solution to all this was to
return the unit &lt;code&gt;nil&lt;/code&gt; to say &quot;it returns nothing&quot; but that just feels a bit
bizzare for sure; why return anything at all? This has real performance
implications as well, because whenever the value of the expression is unused
(which would usually be how these expressions are used) you have a unnecessary 
VM cycle emitting the &lt;code&gt;nil&lt;/code&gt; return value on the stack. Someday, perhaps I could
work in some optimizations to help clean this issue up, but it is an
unfortunate thing that was always gonna be here.&lt;/p&gt;
&lt;p&gt;Similar to the previous point, the next one is: sure, everything evaluates
to something, but you don&apos;t &lt;strong&gt;need&lt;/strong&gt; everything all the time. And whenever
you don&apos;t need something, you have to force it off of yourself because
the language pushes you towards it. That push is for good reason because a
lot of times the expression&apos;s value is something you do need or would be
well off with; but for those cases you have to deal with the whole &lt;code&gt;discard&lt;/code&gt;
fiasco. In the end, this is just another tradeoff.&lt;/p&gt;
&lt;p&gt;There&apos;s the cases where returning values fights the mechanics of the concept
itself, like the &lt;code&gt;save&lt;/code&gt; register with the blocks. Though, this is something
that could be fixed with cleanup in the design of zen in the future; as
a lot of zen&apos;s semantics are still carried over from the statement-based land of
clox.&lt;/p&gt;
&lt;p&gt;The truth of the matter is that full expression-orientedness didn&apos;t turn
out to be as much of an &quot;elegant solution covering all bases&quot; as I imagined,
there are always some ugly corners with everything. However, I still think
the parts that are indeed elegant, and there are a lot, are fully worth all of
it. The whole program is now just one expression; ensuring that a potential type
checker has a very simple job: just evaluate that one expression&apos;s type 
recursively. Very elegant, indeed. But that&apos;s for the next part.&lt;/p&gt;
</content:encoded></item><item><title>converting AD to Bikram Sambat</title><link>https://pes18fan.github.io/posts/ad_to_bs/</link><guid isPermaLink="true">https://pes18fan.github.io/posts/ad_to_bs/</guid><description>Something that every Nepali has to deal with.</description><pubDate>Sat, 23 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In Nepal, the &lt;a href=&quot;https://en.wikipedia.org/wiki/Vikram_Sambat&quot;&gt;Bikram Sambat&lt;/a&gt; calendar
sees very common use. Therefore, conversions between AD and BS are done often,
and a lot of the time people have no idea what is going on under the hood to get
it done. That included me, until yesterday when I made the decision to create a
little terminal app called &lt;a href=&quot;https://github.com/pes18fan/ncal&quot;&gt;ncal&lt;/a&gt;, which is a
very basic, small calendar for BS.&lt;/p&gt;
&lt;p&gt;I plan on adding a few more features and probably
work on a mobile app for a Nepali calendar to learn mobile development as well as
to finally have a non-bloated Nepali calendar app for once.&lt;/p&gt;
&lt;p&gt;Anyways, making this little program gave me some insight into how the conversion is done,
and it&apos;s not pretty, I promise you.&lt;/p&gt;
&lt;p&gt;The process I utilized is one that is used to convert BS to AD, documented in &lt;a href=&quot;https://github.com/bahadurbaniya/Date-Converter-Bikram-Sambat-to-English-Date&quot;&gt;this repository&lt;/a&gt;
and all I did was reverse it for my purposes.&lt;/p&gt;
&lt;h2&gt;initial steps&lt;/h2&gt;
&lt;p&gt;The thing is, the Bikram Sambat is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Lunisolar_calendar&quot;&gt;lunisolar calendar&lt;/a&gt; with a really complicated
set of properties that make determining the length of each month ridiculously difficult
mathematically, unlike AD where each month has a fixed number of days (barring 
February in a leap year). I&apos;m not sure if its impossible or not, but regardless
I didn&apos;t want to put in way too much work for a simple project like this. Perhaps I&apos;ll
try to figure out a way to do it sometime, but not for now.&lt;/p&gt;
&lt;p&gt;So, the only option left is to use a lookup table, which includes the length of
each month in a set of specific years. For ncal, this set is from the year 2000 to 2090.&lt;/p&gt;
&lt;p&gt;A lookup table is also required to get the English date that corresponds to the 
first day of each of the Nepali years that we&apos;ve selected in this set. Data for 
this was only available from 2000 to 2090, so even though the month length set 
was available upto 2100, I only took the set from 2000 to 2090. The month 
lengths being available do mean that I can calculate the first days myself, which
I might do in the future.&lt;/p&gt;
&lt;h2&gt;getting the year&lt;/h2&gt;
&lt;p&gt;This part is the easiest. Just take the Gregorian year and add 56 or 57 to it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nep_year = if gregorian &amp;lt; Globals.first_day[gregorian.year + 57]
             year + 56
           else
             year + 57
           end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whether you add 56 or 57 is based on whether the Gregorian date is before or after
the Nepali new year for its corresponding year. For instance, say we want to convert
the date 2024-1-1 to BS. Adding 57 to its year yields 2081, and the English date for
the first day of that year is 2024-4-13. Obviously, January 1st is before
the 13th of April of 2024, thus our Nepali year is 2024 + 56 = 2080. And if we were
to provide, say, 2024-5-5 as the date, we&apos;d get the correct answer of 2081 as
the year since May 5th is after April 13th.&lt;/p&gt;
&lt;h2&gt;getting the month and day&lt;/h2&gt;
&lt;p&gt;This part is a bit trickier. First, we get the English date for the first day of
the Nepali year we just found out:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;first_day_of_nep_year = Globals.first_day[nep_year]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we find the number of days between the input date and this first date.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# converted to Int32 since the value may come out as either Int32 or Int64
gregorian_day_gap = (gregorian - first_day_of_nep_year).days.to_i32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, loop over the big hash table with the years as the keys and an array of the month
lengths as the values. For each month, subtract the number of days of that month
from your &lt;code&gt;gregorian_day_gap&lt;/code&gt;. Do this till this number is less than the number
of days in the current month.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Globals.month_length[nep_year].each_with_index do |days, i|
  if gregorian_day_gap &amp;lt; days
    nep_month = i + 1
    nep_day = gregorian_day_gap + 1
    break
  end

  gregorian_day_gap -= days
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;m adding 1 to the nep_month since the nep_month is a zero-based index. I&apos;m doing
the same for the nep_day since humans generally don&apos;t call the first day of some month as the &quot;0th&quot;.&lt;/p&gt;
&lt;p&gt;All that&apos;s left is to package this data neatly in any form you like. My code here
is inside of a private function of a class called &lt;code&gt;NepaliDate&lt;/code&gt; and is called by two
of the three constructors used to create a &lt;code&gt;NepaliDate&lt;/code&gt;. It returns the data to them as
a &lt;code&gt;NamedTuple&lt;/code&gt;, which in Crystal is basically a tuple with names for each field.&lt;/p&gt;
&lt;h2&gt;conclusion&lt;/h2&gt;
&lt;p&gt;Well, that&apos;s it! A convoluted process for sure and not one I would recommend trying
to figure out at 12 AM, but in the end it works!&lt;/p&gt;
</content:encoded></item><item><title>Making brightness work on Linux</title><link>https://pes18fan.github.io/posts/brightness_on_linux/</link><guid isPermaLink="true">https://pes18fan.github.io/posts/brightness_on_linux/</guid><description>Quick guide to protect your eyes.</description><pubDate>Wed, 05 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently switched entirely to Linux, specifically Arch, due to issues I had
been facing as of late on Windows. When I tried reinstalling Windows, all my
drivers were gone including wifi drivers and I couldn&apos;t get them working again,
so I decided to just use Linux where the drivers just worked. Funny, considering
I use an NVIDIA GPU.&lt;/p&gt;
&lt;p&gt;Anyways, even before this I had been trying Linux out in dual boot and stuff.
I enjoyed it quite a bit, but there was one glaring issue: brightness couldn&apos;t
be adjusted. There didn&apos;t seem to be any error or anything that described why
this happened, so I just lived with it while using the
&lt;a href=&quot;https://github.com/saifulbkhan/alpha-tint&quot;&gt;AlphaTint GNOME extension&lt;/a&gt; to
artificially change brightness.&lt;/p&gt;
&lt;p&gt;That was until recently. I assume many of you know of &lt;a href=&quot;https://systemd.io/&quot;&gt;systemd&lt;/a&gt;.
It&apos;s a suite of programs that basically opens from the bootloader and starts all
necessary aspects of the system. Some like it, some hate it, I personally don&apos;t
care much. Regardless, the hackerman-like wall of text you get in some
distros when they boot up and shut down is a log of what systemd is doing.&lt;/p&gt;
&lt;p&gt;Within this wall of text, I, by accident, discovered a bit of text that said &quot;backlight&quot; 
and &quot;nvidia&quot;. I instantly realized that this might be a log of an error regarding the
screen backlight, and &lt;a href=&quot;https://www.youtube.com/watch?v=iYWzMvlj2RQ&quot;&gt;of course it&apos;s goddamn NVIDIA&lt;/a&gt;.
I looked into this, and thankfully found a bit of info from the Arch Wiki about this.
I had to set some kernel parameters to get it to work. Set one of these three and
see which one works:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;acpi_backlight=video
acpi_backlight=vendor
acpi_backlight=native
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To set a kernel parameter, first find the configuration file for
your bootloader. In my case, for &lt;code&gt;systemd-boot&lt;/code&gt;, it should be in &lt;code&gt;/boot/loader/entries&lt;/code&gt;.
The bootloader config file&apos;s name seems to depend on the distro. For Arch Linux, 
most sites will say it&apos;s named &lt;code&gt;arch.conf&lt;/code&gt;, but in my case I used &lt;code&gt;archinstall&lt;/code&gt;,
so the file was named something like &lt;code&gt;YYYY-MM-DD_XX-XX-XX_linux.conf&lt;/code&gt;, which includes
the date you installed Arch Linux and the &lt;code&gt;X&lt;/code&gt;s are replaced with some numbers 
that I couldn&apos;t really tell the meaning of. Perhaps they were the time or something.&lt;/p&gt;
&lt;p&gt;In my case, the &lt;code&gt;native&lt;/code&gt; parameter was the one that worked. What a relief.&lt;/p&gt;
&lt;p&gt;If it&apos;s not working for you still, you can check out the whole &lt;a href=&quot;https://wiki.archlinux.org/title/backlight&quot;&gt;backlight page on
the Arch Wiki&lt;/a&gt;. Even if you don&apos;t use
Arch this may be of help.&lt;/p&gt;
</content:encoded></item><item><title>Using btrfs snapshots</title><link>https://pes18fan.github.io/posts/btrfs_snapshots/</link><guid isPermaLink="true">https://pes18fan.github.io/posts/btrfs_snapshots/</guid><description>Avoid losing it all.</description><pubDate>Sun, 23 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { Image } from &quot;astro:assets&quot;;
import systemdJournalError from &quot;../../assets/systemd_journal_liberrors.jpg&quot;&lt;/p&gt;
&lt;p&gt;Yesterday, I spent the whole day fixing a huge mess in my computer, caused due
to circumstances I&apos;ll elaborate on in a bit. Regardless, this all could have
been fixed easily if I made use of the snapshot feature provided by the &lt;code&gt;btrfs&lt;/code&gt;
filesystem, which my device was (and is) using. I learned my lesson, and I
have decided to finally come out of hibernation to teach it to others.&lt;/p&gt;
&lt;h2&gt;So what happened yesterday?&lt;/h2&gt;
&lt;p&gt;I was watching some YouTube, when suddenly, the whole UI froze completely with
the audio still playing. This wasn&apos;t new to me; this is a 
&lt;a href=&quot;https://gitlab.freedesktop.org/drm/amd/-/issues/4141&quot;&gt;known kernel bug&lt;/a&gt; related
to AMD iGPUs, that  is especially prevalent on KDE Plasma, which I was using at 
that time. It had hit my system a few times before too, but I hadn&apos;t bothered 
actually working around it because that would mean disabling some AMD GPU 
features which I did not wish to do. So, like always... I just forced the computer
off via the power key. It turned off succesfully, and I turned it on again to
get back to it.&lt;/p&gt;
&lt;p&gt;Lo and behold, the computer failed to boot up to the GUI. The bootup &lt;code&gt;systemd&lt;/code&gt;
logs were weird; &lt;code&gt;NetworkManager&lt;/code&gt; had failed to start for whatever reason and
instead of the logs clearing the way for SDDM like before, the screen just
got stuck there. After a bit of confusion, I got into a rescue shell and checked
the &lt;code&gt;systemd&lt;/code&gt; journals... then I found this.&lt;/p&gt;
&lt;div&gt;
    
&lt;/div&gt;

&lt;p&gt;All those &quot;file too short&quot; errors you see with the libraries? Seems like that
force shutdown I did before corrupted a bunch of important libraries and
prevented the system from booting entirely. After some extra investigation it
turns out &lt;code&gt;pacman&lt;/code&gt; was doing some updates when I did the shutdown, which might
have caused this, though I&apos;m not sure how exactly.&lt;/p&gt;
&lt;p&gt;Anyways, I desparately tried for the next few hours to fix this problem... to
no avail. Eventually I just had to reinstall everything. Thankfully I had backed
everything up so I didn&apos;t lose much in terms of data, and I also decided to
move to Hyprland for now; I already had a basic config ready beforehand and now
after some extra quality-of-life tweaks to it, I&apos;ve decided to stay with it for
the time being.&lt;/p&gt;
&lt;h2&gt;How I could have fixed it easily&lt;/h2&gt;
&lt;p&gt;I mentioned btrfs before. It is one of the most popular filesystems being used
on Linux systems nowadays. This is mainly because of a snazzy feature it has: 
snapshots. You can think of a filesystem snapshot as a git commit; each snapshot 
corresponds to a particular state of your filesystem, and you can revert to it 
whenever you want. This is a huge win for data protection in case of things 
getting all messed up like what happened to me.&lt;/p&gt;
&lt;p&gt;I was using btrfs at that time... but the problem is, I wasn&apos;t using snapshots
at all. I didn&apos;t even bother. This time though, I decided not to make the same
mistake, and looked into how snapshots are done.&lt;/p&gt;
&lt;p&gt;There are two programs used to set up snapshots that I know of: &lt;a href=&quot;https://wiki.archlinux.org/title/Snapper&quot;&gt;&lt;code&gt;snapper&lt;/code&gt;&lt;/a&gt;
and &lt;a href=&quot;https://github.com/digint/btrbk&quot;&gt;&lt;code&gt;btrbk&lt;/code&gt;&lt;/a&gt;. Looking into these, I found
&lt;a href=&quot;https://ounapuu.ee/posts/2022/07/09/btrbk-is-awesome/&quot;&gt;this post&lt;/a&gt; by Herman Õunapuu,
that described why &lt;code&gt;btrbk&lt;/code&gt; turns out to be better than &lt;code&gt;snapper&lt;/code&gt;. While I&apos;m new to
this and don&apos;t understand it all too well, the basic gist is that &lt;code&gt;btrbk&lt;/code&gt; is
generally better because it is easier to set up and more convinient in some 
ways.&lt;/p&gt;
&lt;p&gt;While Õunapuu also described how to set up &lt;code&gt;btrbk&lt;/code&gt; in their post, said post
assumes you already know a bit about brtfs; so I&apos;ll try to explain it as simply
as possible.&lt;/p&gt;
&lt;p&gt;Firstly, check what subvolumes your system has:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo btrfs subvolume list /
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should get an output similar to this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ID 256 gen 1160 top level 5 path @
ID 257 gen 1161 top level 5 path @home
ID 258 gen 1145 top level 5 path @cache
ID 259 gen 1161 top level 5 path @log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each of these are separate logical partitions in a single btrfs filesystem. They
are NOT separate partitions and don&apos;t create separate block devices (as in those
devices like &lt;code&gt;/dev/sda&lt;/code&gt; or &lt;code&gt;/dev/nvme0n1&lt;/code&gt; that represent storage devices). They
simply separate system data, home data, log data et cetera in one filesystem.&lt;/p&gt;
&lt;p&gt;Now, based on what you have here, you need to create a config file for &lt;code&gt;btrbk&lt;/code&gt;
to tell it how you want it to make snapshots. You can theoretically put this
config anywhere, but the ideal place for it is &lt;code&gt;/etc/btrbk/&lt;/code&gt;. &lt;code&gt;btrbk&lt;/code&gt; provides
a &lt;code&gt;btrbk.conf.example&lt;/code&gt; there already with descriptions of various options and
some useful examples, however it is a bit dense so I&apos;ll show what I personally
use, adapted from Õunapuu&apos;s config.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;timestamp_format        long

volume /
  snapshot_dir /btrbk_snapshots
  subvolume /
    snapshot_preserve_min   12h
    snapshot_preserve       24h
  subvolume /home
    snapshot_preserve_min   48h
    snapshot_preserve       7d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;timestamp_format&lt;/code&gt; is used to determine the name of the created snapshot
subvolumes: for example, with the long format, a snapshot of &lt;code&gt;/home&lt;/code&gt; will look
like &lt;code&gt;home.20250825T1531&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;volume /&lt;/code&gt; tells &lt;code&gt;btrbk&lt;/code&gt; that our filesystem root is at &lt;code&gt;/&lt;/code&gt;. Within it, we
elaborate further. We tell it to put the snapshots in a folder called 
&lt;code&gt;/btrbk_snapshots&lt;/code&gt;, but you can use any folder with any name you&apos;d like. Within,
we now tell it which subvolumes we want to preserve.&lt;/p&gt;
&lt;p&gt;This is where it gets a bit more complicated, with the difference between
&lt;code&gt;snapshot_preserve_min&lt;/code&gt; and &lt;code&gt;snapshot_preserve&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Take the &lt;code&gt;/home&lt;/code&gt; subvolume.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;snapshot_preserve_min&lt;/code&gt; is set to &lt;code&gt;48h&lt;/code&gt;, which means that all snapshots from
  the last 48 hours are kept; no snapshot younger than 48 hours is ever deleted.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;snapshot_preserve&lt;/code&gt; is set to &lt;code&gt;7d&lt;/code&gt;, which means that there is at least one
  snapshot for every day of a weekly schedule.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Imagine you made several snapshots every day for the past week. By the end of
the week, what will happen is that all of those several snapshots you made over
the last two days (last 48 hours) will stay there, and for each of the other days
of the past week, there will be one representative snapshot.&lt;/p&gt;
&lt;p&gt;Similarly, for the &lt;code&gt;/&lt;/code&gt; subvolume in the config, this means that all snapshots 
younger than 12 hours are preserved, and for each hour of the last day, there is
one representative snapshot.&lt;/p&gt;
&lt;p&gt;You can modify these preservation settings as you wish, but this is good enough
for me.&lt;/p&gt;
&lt;p&gt;Now you&apos;re ready to create a snapshot! To test if everything is working, try a
dry run first. It will simply test if all the settings are OK and won&apos;t actually
touch anything.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo btrbk -c /etc/btrbk/btrbk.conf run --dry-run --progress
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If everything is OK, create a snapshot by running&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo btrbk -c /etc/btrbk/btrbk.conf run --progress
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should get an output that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--------------------------------------------------------------------------------
Backup Summary (btrbk command line client, version 0.32.6)

    Date:   Sun Nov 23 21:41:29 2025
    Config: /etc/btrbk/btrbk.conf

Legend:
    ===  up-to-date subvolume (source snapshot)
    +++  created subvolume (source snapshot)
    ---  deleted subvolume
    ***  received subvolume (non-incremental)
    &amp;gt;&amp;gt;&amp;gt;  received subvolume (incremental)
--------------------------------------------------------------------------------
/
+++ /btrbk_snapshots/ROOT.20251123T2141

/home
+++ /btrbk_snapshots/home.20251123T2141
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Automating it&lt;/h2&gt;
&lt;p&gt;It would get pretty annoying to have to manually snapshot things again and again,
and plus you could very well forget to do it. So, it is highly advised to set up
a timer service that will automatically set up snapshots for you. You can use
&lt;code&gt;systemd&lt;/code&gt; timers for this purpose. If you don&apos;t use &lt;code&gt;systemd&lt;/code&gt;, well... you&apos;re
probably capable enough to figure out timers for your specific system yourself.&lt;/p&gt;
&lt;p&gt;If you are indeed using &lt;code&gt;systemd&lt;/code&gt;, first create a system service to create
snapshots by creating a &lt;code&gt;.service&lt;/code&gt; file in &lt;code&gt;/etc/systemd/system&lt;/code&gt;; in my case I
made a file named &lt;code&gt;/etc/systemd/system/btrbk-snapshot.service&lt;/code&gt;. It is a very
simple service that is like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=Run btrbk snapshots

[Service]
Type=oneshot
ExecStart=/usr/bin/btrbk -c /etc/btrbk/btrbk.conf run --progress
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is a basic service that simply executes the snapshot-taking command.&lt;/p&gt;
&lt;p&gt;To make this timed, you have to create a corresponding timer service. Do this by
creating a corresponding &lt;code&gt;.timer&lt;/code&gt; file with the same name as the &lt;code&gt;.service&lt;/code&gt; file;
in my case I created &lt;code&gt;/etc/systemd/system/btrbk-snapshot.timer&lt;/code&gt;. It looks like 
this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=Periodic btrbk snapshots

[Timer]
OnCalendar=*:0/30
Persistent=true

[Install]
WantedBy=timers.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This timer sets the corresponding service, in our case the 
&lt;code&gt;btrbk-snapshot.service&lt;/code&gt;, to run on minute 0 and 30 of every hour, thus 
creating a new snapshot every thirty minutes. The &lt;code&gt;Persistent=true&lt;/code&gt; setting
is used to ensure that if the system was off or the timer missed during any
moment when the service should&apos;ve run, the service will run immediately on
the next boot so that it can &quot;catch up&quot;.&lt;/p&gt;
&lt;p&gt;To get this running, reload the &lt;code&gt;systemd&lt;/code&gt; daemon and enable the service.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl daemon-reload
sudo systemctl enable --now btrbk-snapshot.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you finally have some peace of mind, knowing you can always revert if
your computer ever decides to corrupt all its libraries because of a forced reboot.
Unless it corrupts the snapshots. I hope that&apos;s not a thing.&lt;/p&gt;
&lt;h2&gt;Restoring backups&lt;/h2&gt;
&lt;p&gt;Fortunately, I haven&apos;t had to restore a snapshot just yet. But someday, being a
Linux user, I probably will, so I gotta know how.&lt;/p&gt;
&lt;p&gt;Thankfully, the &lt;code&gt;btrbk&lt;/code&gt; docs tell you how it&apos;s done. The process is described
in detail &lt;a href=&quot;https://github.com/digint/btrbk?tab=readme-ov-file#restoring-backups&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Basically, &lt;code&gt;btrbk&lt;/code&gt; itself does not provide any restoration mechanism so you must
restore manually. The process is basically finding out which snapshot you want
to restore, create a read-write subvolume out of it and get rid of the old broken
subvolume.&lt;/p&gt;
&lt;p&gt;I hope this was helpful!&lt;/p&gt;
</content:encoded></item><item><title>hello</title><link>https://pes18fan.github.io/posts/intro/</link><guid isPermaLink="true">https://pes18fan.github.io/posts/intro/</guid><description>helo world</description><pubDate>Sun, 09 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hello there! I&apos;m pes18fan and I do... well, whatever I like to. I consider myself a pretty impulsive person... not a very good trait for a developer eh? Well, anyways, I&apos;ll just be posting whatever random stuff I get into and want to post here in this blog. I&apos;m a pretty bad developer so I doubt you&apos;ll learn anything out of it, but I hope you do regardless.&lt;/p&gt;
</content:encoded></item><item><title>getting rid of semicolons</title><link>https://pes18fan.github.io/posts/no_semicolons/</link><guid isPermaLink="true">https://pes18fan.github.io/posts/no_semicolons/</guid><description>So you&apos;re creating a programming language and don&apos;t want semicolons? Here&apos;s how to do it.</description><pubDate>Sat, 13 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been working on an interpreter for a simple toy language that I call Kaze recently, following the amazing book &lt;a href=&quot;https://craftinginterpreters.com&quot;&gt;Crafting Interpreters&lt;/a&gt; by Bob Nystrom. In one of the early chapters of the book, Bob states something along the lines of &quot;if you&apos;re building a language today, make sure it doesn&apos;t use semicolons&quot;. But making sure that was the case was trickier than expected. Here&apos;s how I did it.&lt;/p&gt;
&lt;h2&gt;meet Kaze&lt;/h2&gt;
&lt;p&gt;Here&apos;s a simple hello world in Kaze:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;print(&quot;hello, world!\n&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(I know, the syntax highlighting is a bit off, I&apos;m not very experienced with TextMate grammar okay?)&lt;/p&gt;
&lt;p&gt;Anyways, as you can see from this simple example, we don&apos;t need a semicolon. But this one-liner doesn&apos;t quite show what that can mean. Check this fizzbuzz out:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var str = &quot;&quot;

for var i = 1; i &amp;lt;= 100; i = i + 1 
begin
    str = &quot;&quot;

    if i % 3 == 0 then str = str + &quot;fizz&quot;
    if i % 5 == 0 then str = str + &quot;buzz&quot;

    print(str + i + &quot;\n&quot;)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the syntax is kinda like Pascal, but that&apos;s not the point. The point is, we don&apos;t need any semis! How? To find that out, we need to peer into how the language is implemented.&lt;/p&gt;
&lt;h2&gt;no semis? how?&lt;/h2&gt;
&lt;p&gt;You&apos;d think getting rid of semis would be easy, since you&apos;d think &quot;we could just remove them from the grammar!&quot; Not quite. The entire reason semis exist in programming languages is to prevent parser ambiguity. For example, look at this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = 1
-1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What do you think this gets parsed as? Is it two things: &lt;code&gt;var x = 1&lt;/code&gt; and &lt;code&gt;-1&lt;/code&gt;, or &lt;code&gt;var x = 1 - 1&lt;/code&gt;? That&apos;s where this issue lies.&lt;/p&gt;
&lt;p&gt;Languages have tried to get around this in many ways. Python makes newlines and whitespace significant, and JavaScript inserts semicolons behind the scenes. I dislike both approaches, so I went with what Lua, which does not need any special formatting or semis whatsoever, does. For example, this is fully valid:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = 2 b = 3 print(a + b)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will print &lt;code&gt;5&lt;/code&gt;, as expected. How?&lt;/p&gt;
&lt;h3&gt;how do you do it, Lua?&lt;/h3&gt;
&lt;p&gt;There&apos;s an &lt;a href=&quot;https://www.seventeencups.net/posts/how-lua-avoids-semicolons/&quot;&gt;excellent article by 17cupsofcoffee&lt;/a&gt; that fully explains this, but in short, Lua does this by dropping expression-statements. What are those, you ask? An expression statement is simply put, an expression parsed as a statement, which allows you to use an expression (something that returns a value) in a place where you&apos;re supposed to use a statement (something that well... does something). Expression statements could, for example, allow you to use the short circuting behavior of logical operators to execute something conditionally, as in:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;true &amp;amp;&amp;amp; someFunction();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, this is not all they can do.&lt;/p&gt;
&lt;p&gt;Lua doesn&apos;t allow expression statements at all. This makes sure that the ambiguous example stated previously is always parsed as &lt;code&gt;x = 1 - 1&lt;/code&gt;. This does mean that if you need to use an expression statement as above, you&apos;ll need to put it in a temporary variable, like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_ = true and someFunction()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I did the same thing with Kaze, so in that language it would look something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var _ = true and someFunction()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But, there&apos;s still one big issue.&lt;/p&gt;
&lt;h2&gt;return statements&lt;/h2&gt;
&lt;p&gt;The parser issue got somewhat fixed here, but it still remains in a small capacity with return statements.&lt;/p&gt;
&lt;p&gt;Previously, I said that we removed expression statements. Well, not quite. There are actually a few expressions that still should be usable as statements, for example function calls or assignments. Wouldn&apos;t it be awkward to have to assign a variable to even do those? So, I allowed them here too, and now we have a problem. Take a look at this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fun foo
begin
  return
  bar()
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Is this returning nothing or is it returning &lt;code&gt;bar()&lt;/code&gt;? The parser will normally just think that it&apos;s the latter case, but that may not be true. So how do we fix this?&lt;/p&gt;
&lt;p&gt;Lua does so by only allowing return statements at the end of a block. That is, a return statement must always be followed by a corresponding &lt;code&gt;end&lt;/code&gt; statement. Thus, let&apos;s say we have this code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function foo()
  return
  bar()
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As return statements must always be at the end of a block, anything after the return statement must be for that statement. So, the example is parsed as a return statement that returns bar(). How do we implement the ability to check if the return statement is at the end of a block, though?&lt;/p&gt;
&lt;p&gt;In my parser, written in Crystal, this is how I did it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Parse a return statement.
# A return statement must always be at the end of a block.
private def return_statement : Stmt
  keyword = previous
  value = nil
  unless check?(TT::END)
    value = expression

    unless check?(TT::END)
      raise error(keyword, &quot;Return statement must be at the end of a block.&quot;)
    end
  end

  Stmt::Return.new(keyword, value)
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let me explain this a little bit.&lt;/p&gt;
&lt;p&gt;This is a recursive descent parser, i.e. it recursively creates an abstract syntax tree based on the tokens it encounters. This is the function invoked when the parser encounters a &lt;code&gt;return&lt;/code&gt; token, represented by &lt;code&gt;TT::RETURN&lt;/code&gt;. All the tokens are prefixed with &lt;code&gt;TT::&lt;/code&gt; where TT is just an alias for the enum TokenType.&lt;/p&gt;
&lt;p&gt;Anyways, what it does here is that it firstly assigns the keyword token (I keep that to pinpoint the location of a possible error), then it initializes the return value as nil. After that, it checks if the next token is TT::END. If it is, it simply returns a &lt;code&gt;Stmt::Return&lt;/code&gt; object without doing anything else. The interesting thing happens if it doesn&apos;t find that token. Here&apos;s what happens next:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;- It tries to parse whatever is in front as an expression. If it&apos;s a statement, we&apos;ll just get an &quot;Expect expression&quot; error. If it&apos;s not, we&apos;re all good and we keep going.&lt;/li&gt;
&lt;li&gt;- Then it looks for the TT::END token again! If it doesn&apos;t find it yet again, we know that the return statement is definitely not at the block&apos;s end. Thus, we raise an exception.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If everything was all good, we return from the function with the expression to return.&lt;/p&gt;
&lt;p&gt;Pretty cool, right?&lt;/p&gt;
</content:encoded></item><item><title>how to singly linked list??? (in Crystal)</title><link>https://pes18fan.github.io/posts/singly_linked_list/</link><guid isPermaLink="true">https://pes18fan.github.io/posts/singly_linked_list/</guid><description>What the hell is a singly linked list and how the hell do I make it!!???!!</description><pubDate>Mon, 10 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So recently, I found this awesome video by &lt;a href=&quot;https://www.youtube.com/@LowLevelTV&quot;&gt;Low Level Learning&lt;/a&gt;
where he speaks on singly linked lists (unfortunately the video is unavailable as of January 2025).
With my terrible, or should I say, almost non-existent knowledge of data structures,
I decided to follow the video to learn about one of them. The code in the video 
was written in C, however I wanted to be ✨&lt;em&gt;different&lt;/em&gt;✨ and to use another language.
I picked Crystal, mainly because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;- I wanted to use a different language, as I said, so that I don&apos;t end up just copy pasting everything into C.&lt;/li&gt;
&lt;li&gt;- Rust was an alternative but I&apos;m really not good at it at all.&lt;/li&gt;
&lt;li&gt;- I like Crystal and have used it before.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was the start of a very interesting journey (should I even call it journey?
It was done in a few hours, after all...). Well, in either case, I&apos;ll recap what
happened here. While the code might be in Crystal, I&apos;ll be trying to explain it 
in a language-agnostic way, so I hope this helps other people to learn a bit 
about how singly linked lists (I&apos;ll call them SLLs from here since the phrase is too long) work.&lt;/p&gt;
&lt;h2&gt;humble beginnings&lt;/h2&gt;
&lt;p&gt;Everything began with the definition of a &lt;code&gt;struct&lt;/code&gt; for a node. In a SLL, every 
item is called a node, which each has a pointer to the next item in the list. 
This was fairly straightforward:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Node
  property nxt, data

  def initialize(@nxt : Pointer(Node)?, @data : Int32)
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had to name the variable for the next node&apos;s pointer as &lt;code&gt;nxt&lt;/code&gt; since &lt;code&gt;next&lt;/code&gt; is a reserved keyword in Crystal.&lt;/p&gt;
&lt;p&gt;The next thing to do was to create a variable that holds the head of the SLL. The head is a pointer which points to the first node in the list. This is not specific to a node, so it had to be global; however the problem is that Crystal doesn&apos;t have global variables, at least not directly. So, I created a module with a class variable (represented by &lt;code&gt;@@&lt;/code&gt; at the front) which are variables of a class or module itself instead of instances.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module Globals
  extend self

  @@head : Pointer(Node)? = nil

  def head
    @@head
  end

  def set_head(value : Pointer(Node)?)
    @@head = value
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also created a &lt;code&gt;set_head&lt;/code&gt; method so as to change the value of &lt;code&gt;head&lt;/code&gt; from outside the class.&lt;/p&gt;
&lt;h2&gt;user interface&lt;/h2&gt;
&lt;p&gt;Now that the basics were done, the next thing to do was to create a simple UI for the user to figure out how to work with the list.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def print_menu
  puts &quot;You have the following options.&quot;
  puts &quot;\t1. To add a node to the list.&quot;
  puts &quot;\t2. To remove a node from the list.&quot;
  puts &quot;\t3. Insert a node between two nodes in the list.&quot;
  puts &quot;\t4. Print your list.&quot;
  puts &quot;\t5. Quit.&quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Super simple, nothing too fancy.&lt;/p&gt;
&lt;p&gt;I next created the handlers for the options:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;option = -1

while option != 5
  print_menu
  option = gets(chomp: true).to_s.to_i

  if option &amp;gt; 0 &amp;amp;&amp;amp; option &amp;lt;= 5
    case option
    when 1
      # add operation
    when 2
      # remove operation
    when 3
      # insert operation
    when 5
      # do nothing
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;functions&lt;/h2&gt;
&lt;p&gt;Time for the functions. Three functions were implemented in the video, and I&apos;ll go over each one.&lt;/p&gt;
&lt;h3&gt;adding a node&lt;/h3&gt;
&lt;p&gt;Let&apos;s begin with the function to add a node.&lt;/p&gt;
&lt;p&gt;To add a node, we could go in two directions. One would be to add the node to the back, but in that case we&apos;d have to traverse the whole list to find where the list ends, which makes the time complexity &lt;code&gt;O(n)&lt;/code&gt;. That would cause the program to slow down a lot if there are many elements. Thus, it&apos;s a better idea to add to the front, which will provide a constant time complexity of &lt;code&gt;O(1)&lt;/code&gt;. For this, we first allocate the memory for a new node, then check for two edge cases as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;- If the list is empty, i.e. &lt;code&gt;head&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; or in the case of Crystal, &lt;code&gt;nil&lt;/code&gt;, make the head point to the new node instead.&lt;/li&gt;
&lt;li&gt;- If the list is not empty, i.e. &lt;code&gt;head&lt;/code&gt; already points to some node, make the new node pointer point to that node, and then make &lt;code&gt;head&lt;/code&gt; point to the new one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All that can be implemented in a function as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# add a node to the list
def add_node(data : Int32) : Pointer(Node)?
  new : Pointer(Node)? = nil

  # two cases:
  # if the list is empty
  if Globals.head == nil
    new = Pointer(Node).malloc(sizeof(Node))

    return nil if new == nil

    # set the head to point to the new node
    new.value.data = data
    Globals.set_head(new)
    new.value.nxt = nil
    # if list is not empty
  else
    new = Pointer(Node).malloc(sizeof(Node))

    return nil if new == nil

    # set the new node pointer to point to the existing head, then make the head point to it
    new.value.data = data
    new.value.nxt = Globals.head
    Globals.set_head(new)
  end

  return new
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We then add the necessary code into the &lt;code&gt;case&lt;/code&gt; handler to run the function via the UI.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ...
when 1
  # add operation
  puts &quot;What data should I insert?&quot;
  data_to_insert = uninitialized Int32

  loop do
    begin
      data_to_insert = gets(chomp: true).to_s.to_i
    rescue
      puts &quot;Data must be a 32-bit integer. Try again!&quot;
    else
      break
    end
  end

  new : Pointer(Node)? = add_node(data_to_insert)
when 2
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the use of the &lt;code&gt;loop&lt;/code&gt; and the &lt;code&gt;begin&lt;/code&gt; block. I did that in order to handle cases when a non-integer value was inputted, otherwise the program would just crash.&lt;/p&gt;
&lt;p&gt;Easy!&lt;/p&gt;
&lt;h3&gt;removing a node&lt;/h3&gt;
&lt;p&gt;Next, removing a node.&lt;/p&gt;
&lt;p&gt;To remove a node, we need to remove the link that the previous node has with it. We need to make it so that the link points to the element after the one that we are removing, effectively making the node inaccessible. After that, we free the memory of that node.&lt;/p&gt;
&lt;p&gt;In code, it would be implemented as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# remove a node from the list
def remove_node(data : Int32) : Int32
  # set current and previous nodes as head
  current : Pointer(Node)? = Globals.head
  prev : Pointer(Node)? = Globals.head

  # loop while the current and previous nodes are not nil
  while current &amp;amp;&amp;amp; prev
    if current.value.data == data
      # if current node is the list head
      if current == Globals.head
        Globals.set_head(current.value.nxt)
        # if current node is not list head
      else
        prev.value.nxt = current.value.nxt
        # free the memory for current at this point
      end

      return 0 # found the element
    end

    # move each pointer to their next one after every iteration
    prev = current
    current = current.value.nxt
  end

  return 1 # element not found
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function returns a 0 if the node was found and removed, and returns 1 if it wasn&apos;t found. This helps in handling of the function.&lt;/p&gt;
&lt;p&gt;Do note that if you&apos;re using C or any other language without a garbage collector, you need to free the memory for the removed node to prevent a memory leak. Crystal has a garbage collector, so that&apos;s not necessary here.&lt;/p&gt;
&lt;p&gt;We then create the handler for the function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ...
when 2
  # remove operation
  puts &quot;What data should I remove?&quot;
  data_to_remove = uninitialized Int32

  loop do
    begin
      data_to_remove = gets(chomp: true).to_s.to_i
    rescue
      puts &quot;Data must be a 32-bit integer. Try again!&quot;
    else
      break
    end
  end

  return_value = remove_node(data_to_remove)

  if return_value == 1
    puts &quot;Element not found&quot;
  end
when 3
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s it!&lt;/p&gt;
&lt;h3&gt;inserting a node&lt;/h3&gt;
&lt;p&gt;By inserting a node, we mean putting a node in between two existing ones.&lt;/p&gt;
&lt;p&gt;Let&apos;s call the new node N. Say, that the linked list we currently have has three nodes A, B and C with indexes 0, 1 and 2 as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A:0 -&amp;gt; B:1 -&amp;gt; C:2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If N is going to be inserted at position 1, we will insert it in front of B, and then cut the current link between B and C such that the list becomes this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A:0 -&amp;gt; B:1 -&amp;gt; N:2 -&amp;gt; C:3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would be implemented in code as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# insert a node to a position in the list
def insert_node(data : Int32, position : Int32) : Pointer(Node)?
  # set head as current node
  current = Globals.head

  # check if the position is too far into the list
  while current &amp;amp;&amp;amp; position != 0
    position -= 1
    current = current.value.nxt
  end

  if position != 0
    puts &quot;Requested position is too far into the list&quot;
    return nil
  end

  # if the position is in the list, continue as such:
  new : Pointer(Node)? = Pointer(Node).malloc(sizeof(Node))

  return nil if new == nil

  if current
    new.value.data = data
    new.value.nxt = current.value.nxt
    current.value.nxt = new
  end

  return new
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We return the new node if the insertion succeeded, and return &lt;code&gt;nil&lt;/code&gt; if it didn&apos;t, possibly because of the position being too high.&lt;/p&gt;
&lt;p&gt;We handle the function as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ...
when 3
  # insert operation
  puts &quot;What data should I insert?&quot;
  data_to_insert = uninitialized Int32
  position = uninitialized Int32

  loop do
    begin
      data_to_insert = gets(chomp: true).to_s.to_i
    rescue
      puts &quot;Data must be a 32-bit integer. Try again!&quot;
    else
      break
    end
  end

  puts &quot;What position?&quot;
  loop do
    begin
      position = gets(chomp: true).to_s.to_i
    rescue
      puts &quot;Position must be a 32-bit integer. Try again!&quot;
    else
      break
    end
  end

  new = insert_node(data_to_insert, position)

  if new == nil
    puts &quot;Failed to insert into list.&quot;
  end
when 4
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we&apos;re done!&lt;/p&gt;
&lt;h3&gt;printing the list&lt;/h3&gt;
&lt;p&gt;Don&apos;t forget to do this! For this, simply loop over the list and print each node&apos;s data.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# print out the list
def print_list
  current : Pointer(Node)? = Globals.head

  while current
    print &quot;#{current.value.data}-&amp;gt;&quot;
    current = current.value.nxt
  end

  puts &quot;&quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Handle it in the &lt;code&gt;case&lt;/code&gt; block:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ...
when 4
  # print the list
  print_list
when 5
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we&apos;re done! Run the program and check out this fancy new data structure you&apos;ve just learned about.&lt;/p&gt;
&lt;p&gt;If you&apos;re still not sure, you can check out the &lt;a href=&quot;https://gist.github.com/pes18fan/82f2f031bac011c4ae1a401cd602ebf2&quot;&gt;GitHub Gist&lt;/a&gt; for this program.&lt;/p&gt;
</content:encoded></item><item><title>switching to lazy.nvim</title><link>https://pes18fan.github.io/posts/switching_to_lazy/</link><guid isPermaLink="true">https://pes18fan.github.io/posts/switching_to_lazy/</guid><description>Took me long enough.</description><pubDate>Sun, 10 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Been a long time since I posted anything! Well, here I am now, bringing you the
story of how I finally decided to drink the Kool-Aid by switching to &lt;a href=&quot;https://github.com/folke/lazy.nvim&quot;&gt;lazy.nvim&lt;/a&gt;
from packer.&lt;/p&gt;
&lt;p&gt;If you didn&apos;t know yet, yes I use neovim. Some might claim I&apos;m using it just
to look cool, and to be honest I can&apos;t really refute that. Regardless, once I got used to
it I realized there was much more to it than just looking like a CIA hacker.
The efficiency and sheer speed boost you get from switching is &lt;em&gt;&lt;strong&gt;incredible&lt;/strong&gt;&lt;/em&gt;,
but I digress. We&apos;re here to talk about my package manager switch.&lt;/p&gt;
&lt;h2&gt;beginnings with packer&lt;/h2&gt;
&lt;p&gt;When I first started using neovim for real, I watched &lt;a href=&quot;https://www.youtube.com/watch?v=w7i4amO_zaE&quot;&gt;a video by ThePrimeagen&lt;/a&gt;
where he sets up a nice neovim config using packer with lsp, treesitter, you name it. The
configuration was great, I set it up for myself and changed up a few things as
per my needs. It served me well for a long time, and there wasn&apos;t no issue
in particular with it, so one could argue, &quot;why would you need to switch to
lazy in the first place?&quot;&lt;/p&gt;
&lt;p&gt;Apparently, lazy is &lt;strong&gt;much&lt;/strong&gt; faster than packer, and it also has a nice UI along
with a lockfile system to ensure that your configs don&apos;t get messed up if you
switch your device. Those three points were enough to make me switch. Additionally,
packer is not being maintained anymore (which is something I only just realized),
so that&apos;s another reason to switch.&lt;/p&gt;
&lt;h2&gt;getting started&lt;/h2&gt;
&lt;p&gt;First thing I did was create a &lt;code&gt;lazy.lua&lt;/code&gt; file replacing the original &lt;code&gt;packer.lua&lt;/code&gt;
to place the plugin information in. It begins with this bit of bootstrapping code
from the &lt;a href=&quot;https://github.com/folke/lazy.nvim?tab=readme-ov-file#-installation&quot;&gt;lazy.nvim readme&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;local lazypath = vim.fn.stdpath(&quot;data&quot;) .. &quot;/lazy/lazy.nvim&quot;
if not vim.loop.fs_stat(lazypath) then
  vim.fn.system({
    &quot;git&quot;,
    &quot;clone&quot;,
    &quot;--filter=blob:none&quot;,
    &quot;https://github.com/folke/lazy.nvim.git&quot;,
    &quot;--branch=stable&quot;, -- latest stable release
    lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The bootstrapper will ensure that you don&apos;t have to reinstall lazy again if
you&apos;re on a new system, since it will reinstall itself from here.&lt;/p&gt;
&lt;p&gt;I followed this with all of my plugins:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;require(&quot;lazy&quot;).setup({
    -- plugins here...
    -- for example
    &quot;jiangmiao/auto-pairs&quot;,

    -- or for more advanced configuration...
    {
        &quot;nvim-lualine/lualine.nvim&quot;,
        dependencies = { &quot;nvim-tree/nvim-web-devicons&quot; }
    },
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The configuration for adding a plugin is not much different in packer and lazy,
usually its just some different words. You can check out &lt;a href=&quot;https://github.com/folke/lazy.nvim?tab=readme-ov-file#packernvim&quot;&gt;this portion of the
lazy.nvim readme&lt;/a&gt;
for more information about which packer command is which in lazy, or you can
check out &lt;a href=&quot;https://github.com/pes18fan/dotfiles/blob/main/.config/nvim/lua/p18f/lazy.lua&quot;&gt;my own configuration&lt;/a&gt;
as a reference.&lt;/p&gt;
&lt;h2&gt;lazy loading&lt;/h2&gt;
&lt;p&gt;One of the best things about lazy.nvim is in its name; it can load plugins
lazily to improve performance. You can set it so that certain plugins only load
when they are needed in the editor. To do this all you need to do is set &lt;code&gt;lazy&lt;/code&gt;
to &lt;code&gt;true&lt;/code&gt; when adding a plugin:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;nvim-telescope/telescope.nvim&quot;, tag = &quot;0.1.5&quot;,
    dependencies = { &quot;nvim-lua/plenary.nvim&quot; },
    lazy = true,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another useful option is to set the plugin to load only after a certain event
occurs. For instance, you can set GitHub Copilot to only load after you enter
insert mode:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;zbirenbaum/copilot.lua&quot;,
    event = &quot;InsertEnter&quot;,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s also a &lt;code&gt;VeryLazy&lt;/code&gt; option that apparently only loads a plugin once neovim
is fully loaded, but I haven&apos;t tried that out yet.&lt;/p&gt;
&lt;h2&gt;plugin structuring&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/folke/lazy.nvim?tab=readme-ov-file#-structuring-your-plugins&quot;&gt;According to the readme for lazy&lt;/a&gt;, structuring for plugins can be done by using a
Lua module with various files for each module. This is pretty nice since to add
new plugin files all you need to do is add a new file. As I&apos;m adapting 
ThePrimeagen&apos;s setup where he put all his plugin configuration in the nvim/after
folder and it still works with lazy, I don&apos;t see the need to do this myself.
Perhaps there&apos;s better performance or something of that sort, maybe someone can
let me know in the comments.&lt;/p&gt;
&lt;h2&gt;super slow startups&lt;/h2&gt;
&lt;p&gt;After I had set up whatever I had to in lazy and opened neovim with it for the
first time, the startup time seemed to have changed. And not in a good way. In
fact, on checking startup times and comparing with packer&apos;s, I discovered
something horrific.&lt;/p&gt;
&lt;p&gt;Lazy was making neovim load &lt;em&gt;&lt;strong&gt;many, many&lt;/strong&gt;&lt;/em&gt; times slower than packer.&lt;/p&gt;
&lt;p&gt;On checking the startup times, neovim loaded in 65 milliseconds with packer.
But with lazy, it took a ridiculous 1021 (!) milliseconds to load!&lt;/p&gt;
&lt;p&gt;Upon further research, I found that for some reason &lt;a href=&quot;https://github.com/nvim-treesitter/nvim-treesitter&quot;&gt;nvim-treesitter&lt;/a&gt;
was causing the issue. While I tried to figure out how to fix it, eventually the
problem just fixed itself. That was a weird one.&lt;/p&gt;
&lt;h2&gt;get rid of packer remnants&lt;/h2&gt;
&lt;p&gt;One thing that would be good to not forget to do is getting rid of remnants of
packer. If you run &lt;code&gt;:checkhealth lazy&lt;/code&gt; in neovim, you can see that it will tell
you to get rid of the &lt;code&gt;packer_compiled.lua&lt;/code&gt; file and the packer files. Just a
little detail to remember.&lt;/p&gt;
&lt;h2&gt;conclusion&lt;/h2&gt;
&lt;p&gt;Well, despite the &lt;em&gt;really&lt;/em&gt; weird stuff happening at first, switching to lazy
was... pretty easy! I&apos;m not really sure why I was avoiding it so much so far.
I think it&apos;s really nice, and at the end of the day, I did get a performance boost!
With packer, as I stated before it took neovim 65 milliseconds to load, but now
with lazy, I got it down to 46. Not a lot, but it&apos;s an improvement! Perhaps
I might be able to get it down even further later on as I keep using it, since
I&apos;ve seen people load neovim in less than 20 milliseconds with a lot more plugins
than I&apos;m using. Hopefully I can figure out some ways to get that to happen.&lt;/p&gt;
</content:encoded></item></channel></rss>