summaryrefslogtreecommitdiffhomepage
path: root/src/blog/andy-val/index.gsp
blob: bde8e0d218fb98400d755e8b8155b26560b6f0fa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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 constrained 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 m4_abbr(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 }
	}
}