diff options
author | Thomas Voss <mail@thomasvoss.com> | 2023-12-10 02:50:39 +0100 |
---|---|---|
committer | Thomas Voss <mail@thomasvoss.com> | 2023-12-10 02:50:39 +0100 |
commit | 9e337a2309c9dcadd91fb3878c2194c477b92bf7 (patch) | |
tree | 23f5a916acaaa206e3c6b926cceffff51f1bf9a0 /src/blog/andy-val/index.gsp | |
parent | 54219327782db0b5c78aebf2d982f20ea1bef47f (diff) |
Add another Andy post
Diffstat (limited to 'src/blog/andy-val/index.gsp')
-rw-r--r-- | src/blog/andy-val/index.gsp | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/src/blog/andy-val/index.gsp b/src/blog/andy-val/index.gsp new file mode 100644 index 0000000..63f0699 --- /dev/null +++ b/src/blog/andy-val/index.gsp @@ -0,0 +1,160 @@ +html lang="en" { + head { m4_include(head.gsp) } + body { + header { + div { + h1 {-So Much Value} + m4_include(nav.gsp) + } + + figure .quote { + blockquote { + p {= + Complexity is a backdoor and can be misinterpreted as + sophistication. + } + } + figcaption {-asvln.com} + } + } + + main { + h2 #value {-Values} + p {- + As covered in @a href="/blog/new-sh" {-my last post}, I’m working on + Andy, my personal shell. One of the pretty interesting features of Andy + I think is the way that @em{-values} are handled. In the conventional + shell, you can think of a value as a string. In @code{-echo foo}, both + ‘echo’ and ‘foo’ can be thought of as values. Values aren’t just + string-literals though, a Bash process-redirection (@code{-<(cmd)}) is + also a value; typically one that looks like ‘/dev/fd/N’. + } + + p {- + In Andy this mostly holds, with one very important difference: values + are not strings — they’re lists of strings. While the string-literal + @code{-"foo"} might be a string list of length 1, the + process-redirection of @code{-<>{cmd\}} is a list of 2 strings — a path + to a file you can read from, and a path to a file you can write to. + } + + p {- + This is an important distinction to make because of the fact that + value-concatenation goes from a simple string-concatenation to a + cartesian product of the left- and right values: + } + + figure { + pre {= m4_fmt_code(cartesian.an.gsp) } + } + + p {- + This is not just constrainted to literals of course, process + substitutions are also values, so appending each line of a processes + output with a closing-angle-bracket becomes trivial: + } + + figure { + pre {= m4_fmt_code(angle-bracket.an.gsp) } + } + + p {- + Of course you can also append the closing-angle-bracket using + @code{-printf} itself, but it’s just an example after all. + } + + h2 #dynamic {-Dynamic Code} + p {- + What makes Andy different is not just the behavior of values, but where + you can use them. In most shells, various syntactic elements are not + expanded in the same way that regular values are. The following example + results in an error because the function name @code{-$foo} doesn’t get + expanded into @code{-bar}: + } + + figure { + pre {= m4_fmt_code(posix-fail.sh.gsp) } + } + + p {- + Andy doesn’t give a fuck though, it lets you do this if you really want + to: + } + + figure { + pre {= m4_fmt_code(dynamic-func.an.gsp) } + } + + p {- + Now you might think that this is really retarded — especially if you’re + a Rust developer — and you know what? I did too! But you know what the + beauty of recreational programming is? It doesn’t @code{-have} to be + good — and once you learn to ignore the low-value morons that will + see you experimenting with new ideas and call it garbage — you can start + to actually develop some pretty neat solutions to problems. + } + + p {- + For example, Andy like all shells implements support for signal + handling. In the POSIX shell this is done via the ‘trap’ builtin + function: + } + + figure { + pre {= m4_fmt_code(trap.sh.gsp) } + } + + p {- + Now personally, I really don’t like @code{-trap}. Having to write your + signal handling code in string is just not very ergonomic. You + @em{-can} put your code in a function that you then call in your signal + handler, but I think Andy can do better. The way you handle a signal in + Andy is to simply define a function of the name ‘sigX’ where ‘X’ is the + name of the signal. If you want to write a handler for SIGINT, you + simply define the ‘sigint’ function! + } + + p {- + What if we want to handle multiple signals though? In the above + example, we deleted some temporary files on SIGQUIT and also on the + special SIGEXIT (a fake signal that signifies that the process is + exiting). Well the naïve solution is to simply define a handler + function that you call from your handlers: + } + + figure { + pre .sh {= m4_fmt_code(naïve.an.gsp) } + } + + p {- + But wait… remember how we can dynamically define functions? What if we + just… dynamically define our signal handlers: + } + + figure { + pre .sh {= m4_fmt_code(smart.an.gsp) } + } + + p {- + Personally, I think that’s pretty neat, and I like it! Of course some + Haskell developer that’s never @em{-actually} written any working + software will probably be sending me an angry email right about now + about how this is unsafe code and blah blah, but I don’t care. + } + + p {- + I suppose while this post is primarily about just showing off some of + Andy’s behavior, I also want to make the point that it’s always a good + idea to just try new ideas, no matter how contrary they are to ‘best + practices’ and Rust developers. Don’t waste your time worrying about + what other people think about what you make; I used to do that, and it’s + not great. Just make shit, even if it’s weird. You might just + accidentally add a cool new feature you actually kind of like. + } + } + + hr{} + + footer { m4_footer } + } +} |