summaryrefslogtreecommitdiff
path: root/src/blog/best-tool-for-the-job.kuht
diff options
context:
space:
mode:
Diffstat (limited to 'src/blog/best-tool-for-the-job.kuht')
-rwxr-xr-xsrc/blog/best-tool-for-the-job.kuht685
1 files changed, 685 insertions, 0 deletions
diff --git a/src/blog/best-tool-for-the-job.kuht b/src/blog/best-tool-for-the-job.kuht
new file mode 100755
index 0000000..ad89290
--- /dev/null
+++ b/src/blog/best-tool-for-the-job.kuht
@@ -0,0 +1,685 @@
+<import "base.kuht" as "base" />
+
+<head>
+ <title>The Best Tool for the Job</title>
+ <meta name="description" content="What language should you use for different tasks? Don't follow my advice" />
+</head>
+
+<body>
+
+<article>
+
+<h1>The Best Tool for the Job</h1>
+
+<p>
+ I often see programmers saying that programming languages are tools, and that instead of arguing over
+ what the best language is, we should just pick the right tool for the job. I think this is a sensible
+ philosophy, but I've never seen it used in a sensible way. One time I was talking to someone who told
+ me that the Software Engineering program at RIT exclusively uses the Java programming language. When
+ I pointed out how weird this was, he said, "Languages are tools, and you should just use the right
+ tool for the job"<a id="af-1" href="#footnote-1"><sup>1</sup></a>. If Java is a hammer in this
+ analogy, then why is this major not teaching you how to use a screwdriver?
+</p>
+
+<p>
+ There are other times when the philosphy is used in sort of the right way, but to promote a language
+ that doesn't fit at all. I keep seeing Go being recommended for web development, for example. That is
+ the intended purpose of the language, but I don't recommend it due to the strange way it handles
+ errors<a id="af-2" href="#footnote-2"><sup>2</sup></a>.
+</p>
+
+<p>
+ To be clear, I don't hate this way of thinking. I approve of it. In fact, I find the suggestion that
+ we should just use the same language for everything even worse. But I wish more thought went into the
+ idea. In this article, I want to propose the actual ideal language for each task. Surely, this will
+ end all debates related to the choice of programming language. If you currently in the middle of such
+ a debate, link this article to your co-workers<a id="af-3" href="#footnote-3"><sup>3</sup></a>, and
+ give yourselves the rest of the day off.
+</p>
+
+<h2>My Biases</h2>
+
+<p>
+ Just like everybody else, I have my own preferences for language design, which might not align with
+ yours. I still think my recommendations are correct, but I'll make some premises clear here, so that
+ if you want to adapt my work for your preferences, you may do so.
+</p>
+
+<ul>
+ <li>
+ I hate dynamic typing. The time it takes me to ensure type safety is far less than the time it
+ takes me to debug type errors, find the documentation that tells me what types I can pass into
+ a function, figure out what methods I can call on a given type, or even what type a given value
+ is supposed to be. Ensuring type safety doesn't take that much time anyway, as long as your
+ language has type inference. Most of the languages I recommend here have type inference.
+ </li>
+ <li>
+ On that note, the language should try to prevent me from making runtime errors. No language can
+ fully prevent logic errors, but you shoud note that most, if not all, of the languages I
+ recommend have some sort of null safety, albeit not always by default.
+ </li>
+ <li>
+ Similarly to the last two points, borrow checking provides many benefits (including
+ <a href="/blog/how-happylock-works.html">deadlock prevention</a>) which I believe are worth the
+ hassle. I don't recommend a borrow checker all of the time, but it is a pretty common occurrence.
+ </li>
+ <li>
+ I don't like object-oriented programming. I don't have a problem with classes. But when a
+ language forces me to put all of my behavior inside of a class, I get annoyed. I also recommend
+ against using inheritance. I can put up with languages like C#, but it's not an ideal situation.
+ </li>
+</ul>
+
+<p>
+ Something else that I think is worth pointing out is that I will be focusing more on the language
+ features than the ecosystem. In a more practical discussion, you should probably be thinking about
+ this. But I think the language features are more interesting, and it's not hard to imagine a good
+ ecosystem being developed for any of these languages. Sometimes, a language feature means it might
+ be easier to develop an ecosystem designed for a specific task.
+</p>
+
+<h2>Systems or Embedded Programming</h2>
+
+<p>
+ You'll need Assembly. There's no getting around that. The question is, what language do you use to
+ call your Assembly utilities? Given that most programming languages need a runtime, and that the
+ runtime does not exist until you write it, you have a limited set of options:
+</p>
+
+<ul>
+ <li><strong>More Assembly:</strong> Yuck. Do I need to explain this one?</li>
+ <li><strong>C:</strong>
+ An oldie. Not quite a goodie. I think we can do better than 30 years of tech debt.
+ </li>
+ <li><strong>C++:</strong>
+ Not much better than C. Linus Torvalds would actually prefer C. There's a lot of language bloat,
+ and creating a custom standard library for C++ is much harder than for C.
+ </li>
+ <li><strong>Rust:</strong>
+ Not exactly made for kernel development. The Linux kernel can use it now, but they modified some
+ of the standard library features in order to make it work.
+ </li>
+ <li><strong>Zig:</strong> This language isn't even 1.0 yet.</li>
+</ul>
+
+<p>
+ I recommend <strong>Rust</strong>. Most of the errors in many projects are due to memory safety issues.
+ <a href="https://security.googleblog.com/2022/12/memory-safe-languages-in-android-13.html">
+ Android has solved this problem by using more memory safe languages</a>.
+ Windows and Linux are also starting to introduce Rust into their codebases for the same reason.
+</p>
+
+<p>
+ I also mentioned having to write a runtime for C and C++. That's not necessary for Rust. Rust has, in
+ addition to the standard library, a core library. The core library does not require an operating
+ system, and has some very convenient utilities,
+ <a href="https://doc.rust-lang.org/stable/std/primitive.str.html#method.encode_utf16">including UTF-16</a>.
+ And even without the standard library, you can use the built-in alloc crate to use utilities that require
+ memory allocation. You'll just need to bring your own memory allocator.
+</p>
+
+<p>
+ I'm not done yet. Rust has async/await. Rust's implementation of async/await does not rely upon the
+ existence of an operating system. You can create your own async runtime, and then use async/await
+ inside of your kernel! If that does not sell you, nothing will.
+</p>
+
+<h2>Parsers and Compilers</h2>
+
+<p>
+ Now you have an operating system. Maybe you already have a shell and some command-line utilities. But
+ you now you need a way to compile programs on your new system. You don't necessarily want to write
+ everything in Rust. But you'll need to write a compiler for that to work. What language do you do it
+ in? I have a couple ideas for a good parser language. I'll make the case for both.
+</p>
+
+<h3>Rust</h3>
+
+<p>
+ There's one particular reason why I like Rust here: enums. Rust enums are discriminant unions, which
+ can make it much easier to create a token type, and an abstract syntax tree. Here's an example of
+ some types I'd make when creating a basic Lisp interpreter:
+</p>
+
+<pre>
+enum TokenType {
+ LeftParenthesis,
+ RightParenthesis,
+
+ LiteralNumber(f64),
+ LiteralString(Arc&lt;str>),
+ Symbol(Arc&lt;str>),
+}
+</pre>
+
+<p>And here's what the abstract syntax tree could look like:</p>
+
+<pre>
+enum ExpressionType {
+ LiteralNumber(f64),
+ LiteralString(Arc&lt;str>),
+ Symbol(Arc&lt;str>),
+
+ SExpression(Arc&lt;[ExpressionType]>),
+}
+</pre>
+
+<p>
+ It gets more complicated than that when you're doing error reporting, but you can at least understand
+ how this makes things easier. I also recommend you take a look at the
+ <a href="https://en.wikipedia.org/wiki/Icon_(programming_language)">Icon programming language</a>.
+ You won't be using it, but it is a very useful framework for tokenizing. I wrote a Rust
+ implementation of some of Icon's standard library, in the form of a library called
+ <a href="https://www.lib.rs/snob">snob</a>. I still use it, and I recommend giving it a try.
+</p>
+
+<p>
+ I don't think Rust is the perfect language for this task though. You may have noticed all of my
+ <code>Arc</code>s in that code snippet. Constantly copying a <code>Box</code> or a <code>Vec</code>
+ will get expensive very quickly. But this interface isn't the greatest. Constantly copying an
+ <code>Arc</code> isn't great either. In fact, the D programming language found that typical
+ generational garbage collection is faster than implementing garbage collection using
+ <code>Arc</code><a id="af-4" href="#footnote-4"><sup>4</sup></a>. Another problem is that Rust uses
+ more memory than a garbage collected language would. Rust monomorphizes generic functions for more
+ speed. But this bloats the binary size and uses a lot of memory. I can't find it now, but I remember
+ seeing a GitHub issue stating that rustc couldn't be compiled on a Raspberry Pi, because compiling it
+ requires too much memory.
+</p>
+
+<p>
+ So I would recommend a garbage collected language, but the problem is that I can't find a language
+ that has Rust-style enums and garbage collection<a id="af-5" href="#footnote-5"><sup>5</sup></a>. C#
+ could do this once they figure out <a href="https://github.com/dotnet/csharplang/issues/113">discriminated unions</a>.
+ But I don't hold much hope for that right now.
+</p>
+
+<h3>Dart</h3>
+
+<p>
+ Now I'll admit that I've never attempted to write a parser in Dart before. I'm only thinking about it
+ now. Dart doesn't have Rust-style enums. But it tried. Dart has sealed classes, and if you run a
+ switch statement on a sealed class, then it will require an exhaustive match. Here, I'll duplicate
+ the Token data structure I showed in Rust:
+</p>
+
+<pre>
+sealed class Token {}
+
+class LeftParenthesis extends Token {}
+
+class RightParenthesis extends Token {}
+
+class LiteralNumber extends Token {
+ final double value;
+
+ const LiteralNumber(this.value);
+}
+
+class LiteralString extends Token {
+ final String value;
+
+ const LiteralString(this.value);
+}
+
+class Symbol extends Token {
+ final String name;
+
+ const Symbol(this.name);
+}
+</pre>
+
+<p>
+ Notice how that was far more verbose than the Rust implementation. And I didn't even create an
+ abstract syntax tree. We're also using inheritance, so we'll have to be careful about how we modify
+ the base class. But there is one benefit. Something I did not show in the Rust implementation is the
+ information about where the data is stored in the original source code. Adding that to the Rust
+ implementation can be done using another struct.
+</p>
+
+<pre>
+struct Token {
+ token_type: TokenType,
+ start: usize,
+ end: usize,
+ file: Arc&lt;Path>,
+}
+</pre>
+
+<p>
+ Then any token we create has to be wrapped in this new structure. But in Dart, we can do it by
+ modifying the base class
+</p>
+
+<pre>
+sealed class Token {
+ final int start;
+ final int end;
+ final File file;
+
+ const Token({required this.start, required this.end, required this.file});
+}
+</pre>
+
+<p>
+ But then we'll also have to modify the constructors on the child classes to have the same
+ information. So you might still want to use composition here. But this would cut down on the of times
+ you have to write something like <code>token.token_type</code>.
+</p>
+
+<p>Dart seems promising. I'll try writing a parser in it sometime.</p>
+
+<h2>Scripts</h2>
+
+<p>
+ So now you can create a parser. But a parser for what language? Say you need a scripting language for
+ some simple tasks. What can you use?
+</p>
+
+<p>
+ Bash is ok, if the only thing you need to do is run some simple commands. But most scripts will end
+ up being more complicated than that. If your bash script needs a variable, then you shouldn't use
+ Bash.
+</p>
+
+<p>
+ Here we have a different set of priorities. This code shouldn't take too long to write. In many cases
+ it will be thrown away immediately. We should still make it easy to read, just in case. But we might
+ not need it for very long.
+</p>
+
+<p>
+ <strong>Python</strong> was designed to be a middle-ground between Bash and C. It's painfully slow,
+ but our scripts shouldn't take too long to run anyway. The syntax is kinda annoying, but not annoying
+ enough that I would consider writing a new language to replace it. There are some conveniences
+ like default parameters that I honestly wish Rust had, but that one in particular was implemented
+ with some stupid mutability rules. There's no interfaces, only inheritance.
+ <a href="https://www.youtube.com/watch?v=sbVxq7nNtgo">Exceptions are bad</a>. After getting used
+ to the borrow checker, I don't appreciate the <code>with</code> blocks. So much of the standard library
+ is deprecated. Python is certainly not the perfect language.
+</p>
+
+<p>
+ But it does come with some interesting ideas. Gradual typing was probably the right decision for a
+ language like this. And I can't complain, because the type system comes with null safety. The idea of
+ having big integers by default is also really clever. And the large set of builtins is both a
+ blessing and a curse. It's a good idea for scripting, but not when half of the builtins are
+ deprecated. The package manager installs everything globally, which is not a great idea, but nothing
+ else really makes sense for a scripting language. Most scripts don't need their own directory and
+ <code>node_modules</code>.
+</p>
+
+<p>
+ That said, there's more that could be done. I don't like whitespace as syntax, but maybe we could
+ agree on automatic-semicolon-insertion as a middle ground. A derive-like syntax seems especially
+ important for a language trying to cut down on developer time. This could even be done with macros.
+ Speaking of which, I'd like to see interfaces, and Rust's enums. Dart has some nice ideas too, like
+ the <code>this.x</code> parameter in constructors, as well as named constructors and factories. Hell,
+ use 1-based-indexing and use a numerical tower as the default number type, or at least use a decimal
+ type. Python is used mostly by novices, so we might as well make it easier for them.
+</p>
+
+<p>
+ At this point, I've essentially proposed my own programming language. To be clear, I don't hate
+ Python enough to actually make this. I'm just dreaming here.
+</p>
+
+<h2>Game Development</h2>
+
+<p>
+ Now you want to have some fun, so why not make a video game? What language can we use for that?
+</p>
+
+<p>
+ There are several constraints that game engines have that other programs do not. For example, we
+ really do not want any stuttering, so a garbage collector is out of the question, unless you decide
+ to manually run the garbage collector on every single frame. However, performance is another
+ important goal for a video game, so we probably want to avoid that too. That means
+ <strong>Rust</strong> is our only option.
+</p>
+
+<p>
+ The problem there is that the ecosystem for Rust game development
+ <a href="https://www.arewegameyet.rs">isn't very ripe</a>. There is the work-in-progress
+ <a href="https://bevyengine.org/">Bevy</a> game engine, but I honestly don't like the way you query
+ for components in it. So you'll be making your own game engine. Given that this is Rust, we will
+ still use an entity-component system. But the queries will be much simpler:
+</p>
+
+<pre>
+let mut player_sprite = cx.sprites.get("player")?;
+let player_collider = cx.colliders.get("player")?;
+player_sprite.set_position(player_collider.position());
+</pre>
+
+<p>
+ It would be really cool if the engine supported hot reloading scripts, but Rust doesn't have that.
+ We could make it work by compiling the scripts to WebAssembly and reloading them when the file
+ changes. But this would introduce undefined behavior if the old script's state doesn't match what the
+ new script is expecting (i.e. a struct definition changed). To resolve this, we will require that
+ scripts not store any state. Instead, the memory of the script will be cleared every frame. Any
+ information we need to keep will be stored in the engine code.
+</p>
+
+<pre>
+fn run(state: ScriptState) {
+ let mut velocity: i32 = state.get("player_velocity").parse();
+ velocity += 1;
+ state.store("player_velocity", velocity);
+}
+</pre>
+
+<p>
+ Now, something magical has just happened. We are clearing all state every single frame. All
+ heap-allocated memory, as far as this script is concerned, is static. This will be the new
+ memory-allocation strategy. We can use a bump allocator as the allocator. And that means we won't
+ need a garbage collector (because we're clearing memory every frame), or a borrow checker
+ (because all heap memory is static). It wouldn't be too hard to adapt an existing garbage-collected
+ language to use this strategy, so we could just pick one that can theoretically be compiled to
+ WebAssembly, and make a compiler that doesn't insert a garbage collector. That could include C#,
+ Dart, Kotlin, or Swift. But we could also make a custom language suited to this task. This could
+ make it even faster than Rust, because bump allocators are so fast.
+</p>
+
+<p>
+ In fact, I don't think the perfect language for game development exists yet. I want something easy to
+ embed, like Lua, but statically typed<a id="af-6" href="#footnote-6"><sup>6</sup></a>. If we could
+ block the use of the file system or network, then we could ensure that mods don't contain any
+ viruses. But the language server would need some way to know what libraries are being imported, and
+ what they contain.
+</p>
+
+<p>Another useful idea is a property declaration. Here's an analogy:</p>
+
+<pre>
+// this is determined at compile-time, and not modifiable at runtime
+const PLAYER_SPEED: usize = 5;
+
+// this can be mutated (not in Rust, but in other languages), but an initial value must be given
+static PLAYER_SPEED: usize = 5;
+
+// this is a value inserted by the game engine
+property PLAYER_SPEED: usize;
+</pre>
+
+<p>
+ The engine would need to parse the script to determine what properties exist, give the developer a
+ slider that they can use to change it, and then insert that value into the script. Technically, this
+ would probably just be syntactic sugar for calling a function. But you wouldn't have to worry about
+ typos in the property name.
+</p>
+
+<p>
+ I'm sure are other features that I think could be useful as well, but this proves the point that a
+ language specific to game scripting could be useful.
+</p>
+
+<h2>Backend Web</h2>
+
+<p>
+ Now you have a video game. Maybe this game has online features. In that case, you'll need a web
+ server to handle communication between players. Once again, we need to pick a language to write it
+ in. Let's take a look at the usual suspects.
+</p>
+
+<h3>Rust (or Swift)</h3>
+
+<p>
+ Rust is great because of the exceptional error handling. If all errors are handled using the
+ <code>Result</code> type, and errors are sent to the client using the <code>Result</code> type,
+ then you guarantee no cryptic 500 errors, except in the case of a panic. Swift also has a Result
+ type, so most of what I say here should apply to Swift as well (although I'm not familiar enough
+ with Swift to tell you when this advice might not apply).
+</p>
+
+<p>
+ A point to Rust specifically is the performance and lack of a garbage collector. In most languages,
+ when the garbage collector starts, the entire program stops. But Rust doesn't have this problem, so
+ you can send responses at the speed of the network. Another point to Rust is the fearless
+ concurrency. No data races ever, and bad race conditions should be rare as well. Multithreading is
+ important to a server, so this is a good thing to have. The ecosystem is also able to provide things
+ that no other language can. <a href="https://www.lib.rs/serde">Serde</a> makes JSON and XML
+ serialization/deserialization a piece of cake. Take a look at <a href="https://lib.rs/sqlx">SQLx</a>.
+ Not only can Rust get rid of memory and race errors, it also gets rid of SQL errors.
+</p>
+
+<p>
+ AWS supports Lambda functions written in Rust, and even gives you a discount if you do so, since it
+ runs faster. If you don't like cold boots, then you can always use an actual server application.
+ There are a few backend web frameworks written in Rust, such as <a href="https://www.lib.rs/axum">axum</a>,
+ which everyone seems to love despite the fact that it isn't finished yet. I use
+ <a href="https://www.lib.rs/actix-web">actix-web</a>. It's slightly faster than axum, and it's both
+ stable and production-ready.
+</p>
+
+<h3>Dart</h3>
+
+<p>
+ Let's start with what Dart doesn't have. It doesn't have anything as nice as serde or SQLx because it
+ doesn't have macros. It also doesn't have a <code>Result</code> type. AWS Lambda support for Dart is
+ also lacking. So all in all, I recommend <strong>Rust</strong> over Dart. But I wanted to mention
+ Dart because of the interesting strategy it uses to avoid data races.
+</p>
+
+<p>
+ Dart doesn't allow you to share memory between threads. Each thread is its own <code>Instance</code>
+ with its own heap. But the <code>Instance</code>s can communicate with each other over channels. So
+ there are no data races, just like Rust.
+</p>
+
+<p>
+ Now, Dart does have a garbage collector. It's always going to be slightly slower than Rust under
+ load. But since all of the threads have a separate heap, the garbage collector does not need to stop
+ your application from running. It only stops one thread. And the garbage collector can be programmed
+ to run early if a thread has no tasks waiting for it. So in practice, you might never experience a
+ GC pause.
+</p>
+
+<p>
+ There are a number of Dart backend frameworks. I haven't personally tried any of them, and many of
+ them are deprecated, but I think it's worth checking out if you really want to avoid garbage
+ collection. Some of them have built-in ORMs and I've seen at least one with built-in OAuth2.
+</p>
+
+<h3>WebAssembly</h3>
+
+<p>
+ In case you didn't guess, we're going down the same path I followed in my game development ideas.
+</p>
+
+<p>
+ The framework itself should be written in Rust, but we can do better for the route code. I really
+ like the idea of serverless, but cold starts are a problem for me. But take a look at Fermyon's
+ <a href="https://www.fermyon.com/spin">spin</a>. It uses WebAssembly to get the best of both worlds.
+ Just like serverless functions, they don't need to be loaded up all of the time. But because
+ WebAssembly is already heavily sandboxed, they don't need to create a virtual machine to contain your
+ code. Which makes the cold starts fast. So fast that they've decided to just cold start every single
+ time. With the help of WASI, Spin can support other languages, such as Go, Python, JavaScript, C#,
+ and Ruby.
+</p>
+
+<p>
+ So you might see where this is going. Again, we can use a bump allocator to avoid garbage collection
+ and borrow checking. I think spin uses a custom implementation of WASI in order to keep everything
+ sandboxed, so we could do that too. But, again, custom compilers would be needed, so we could forego
+ WASI and use a more stable interface that we invent.
+</p>
+
+<p>
+ I also think that a new language to support this system could be helpful. We'll need Rust macros,
+ Rust enums, and a <code>Result</code> type. Although, the macros would probably be implemented in the
+ embedding, in order to make it more powerful. We'll also want a multithreading system similar to the
+ one in Dart. Of course this new language would need async/await. Ideally, it would also have a core
+ library separate from the main one. Ideally, it would also be impossible to panic, but that seems
+ impossible.
+</p>
+
+<p>
+ There's something else we can do now that we're running everything inside of a WebAssembly framework.
+ Spin provides its own key/value store. The neat thing about this is that we don't necessarily need a
+ network request to access this store. It's stored locally on the machine. So we can do fast DB access
+ by creating our own database, and embedding it directly into the framework. I think Spin had this
+ idea with the built-in SQLite database, but
+ <a href="https://www.sqlite.org/whentouse.html#situations_where_a_client_server_rdbms_may_work_better">
+ SQLite doesn't work well in some cases
+ </a>.
+ The key/value store Spin provides is also limited to about 1000 tuples, if I understand correctly.
+ So I would prefer something with a built-in wide column DB, that doesn't require a network connection
+ to access. Maybe also a relational database, but those are harder to create from scratch (speaking
+ from experience).
+</p>
+
+<p>
+ I'm not actually convinced that WebAssembly is the best possible thing here either. WebAssembly is a
+ stack-based bytecode, which is typically slower than one based on registers. This is a good idea for
+ WebAssembly, because it's on the web, and stack-based bytecode is much smaller than register-based.
+ So you'll be able to download the code more quickly. But a server doesn't have this requirement. The
+ bytecode will need to be uploaded to the server, but the size doesn't matter too much after that. So
+ I could imagine a new bytecode being invented here that takes advantage of registers. It could even
+ be AOT-compiled once it reaches the server. The same idea applies to games as well. The only benefit
+ to a smaller bytecode would be a smaller download size, but performance is probably more important
+ there.
+</p>
+
+<h2>Frontend Web</h2>
+
+<p>
+ Any language you choose here is going to either compile into JavaScript or WebAssembly. WebAssembly
+ can be a problem for many languages, since you need glue code to make the language's runtime work
+ in a browser, which is often the same size as a JavaScript framework. If there were a language that
+ had a runtime more directly based on JavaScript, designed specifically for frontend WebAssembly, that
+ could work<a id="af-7" href="#footnote-7"><sup>7</sup></a>. But until that happens, I'm going to
+ recommend avoiding this approach.
+</p>
+
+<p>
+ That leaves your options to JavaScript, and languages that compile to JavaScript. This shouldn't be
+ a surprise to anyone, but <strong>TypeScript</strong> is your best bet here. I don't think it's
+ perfect. It takes some of the flaws of JavaScript for itself. But it does make many of them more
+ bearable. It doesn't cause the JavaScript to get much larger. You get built in lowering to older
+ versions of JavaScript. And you inherit the whole JavaScript ecosystem.
+</p>
+
+<p>
+ Alright, here's the part where I propose a better language. First of all, we shouldn't be inheriting
+ JavaScript's syntax. If we have to compile to JavaScript from another language, why not compile from
+ a good one? So things like <code>===</code> don't need to be there. We should also get rid of
+ <code>undefined</code>.
+</p>
+
+<p>
+ Now, one problem I've noticed in most JavaScript frameworks is the difficulty in figuring out which
+ variables have been updated. Svelte tried to solve this by using the equal sign as a marker that a
+ variable needs to be updated. But what if I use <code>array.push(5)</code>? Now I need to enter a
+ hacky <code>array = array</code> to make the change visible. So we'll want some way of detecting
+ which methods mutate a value.
+</p>
+
+<p>
+ Rust, as you may know, has that. Methods that mutate the value take <code>&mut self</code>. How would
+ we implement this system in Rust?
+</p>
+
+<pre>
+struct Signal&lt;T> {
+ value: T,
+}
+
+struct SignalGuard&lt;'a, T> {
+ signal: &amp;'a mut Signal&lt;T>,
+}
+
+impl&lt;'a, T> Drop for SignalGuard&lt;'a, T> {
+ fn drop(&amp;mut self) {
+ mark_as_updated(self.signal);
+ }
+}
+</pre>
+
+<p>
+ We won't be able to replicate this in TypeScript because TypeScript doesn't have a borrow checker,
+ and thus cannot detect when a value needs to be dropped. That's the problem that the garbage
+ collector is meant to solve. So how about we add a borrow checker to TypeScript?
+</p>
+
+<p>
+ Yes, I'm actually proposing taking a language which does not have or need a borrow checker, and
+ adding a borrow checker to it. The borrow checker just makes so many problems easier. For instance,
+ we said that we needed a language that compiles to WebAssembly but is more closely related to
+ JavaScript. If we have a borrow checker, and plan the language properly, this language could even
+ compile to WebAssembly, making it faster than JavaScript.
+</p>
+
+<p>
+ I've been working on a web framework for a bit now, and I think this would be a useful feature. I
+ think I'll try it someday.
+</p>
+
+<h2>Conclusion</h2>
+
+<p>
+ I didn't mention desktop or mobile apps, but the answer there would be incredibly simple: Dart. Dart
+ was designed with that specifically in mind, and went through extensive user testing to ensure that
+ it could be useful for that task. I wouldn't even have any suggestions for what to add to it
+ (other than Rust enums and macros). Other languages try to have more general purposes, but if a
+ language is built with a specific goal in mind, it will be good for that task. Python was designed
+ with scripts in mind, and I recommended it, even though I hate dynamically typed languages. Rust was
+ designed as a competitor to C++, and it does indeed replace C++ in many use-cases. Icon is a language
+ that barely exists, and yet I still gave it a shoutout in the parser section.
+</p>
+
+<p>
+ The idea of languages being tools is a good one. But I don't think enough thought goes into it. Make
+ a language with a task in mind. It will be a great one.
+</p>
+
+</article>
+
+<hr />
+
+<footer>
+ <ol>
+ <li id="footnote-1">
+ This guy is a jerk, for the record. This is not an important detail for the story. But I did
+ want the world to know that. <a class="return" href="#af-1">return</a>
+ </li>
+ <li id="footnote-2">
+ I recommend reading some of Amos' articles on Go. The best one is
+ <a href="https://fasterthanli.me/articles/abstracting-away-correctness">Abstracting away correctness</a>,
+ but there's <a href="https://fasterthanli.me/tags/golang">other good ones too</a>.
+ <a class="return" href="#af-2">return</a>
+ </li>
+ <li id="footnote-3">
+ If you want to be sneaky, read ahead and make sure I'm confirming your biases before linking
+ here. Either way, you're boosting engagement, so I don't mind. If you're a Rust diehard, I'll
+ save you the time and tell you that I recommend Rust for most things. As you read, you'll
+ probably notice that following my advice might not be in your company's best interest.
+ <a class="return" href="#af-3">return</a>
+ </li>
+ <li id="footnote-4">
+ This criticism doesn't apply to most Rust codebases, because most codebases don't use
+ <code>Arc</code> on every single variable. <a class="return" href="#af-4">return</a>
+ </li>
+ <li id="footnote-5">
+ You might be wondering why I didn't mention functional languages here. The main reason is
+ that I don't think I'm familiar enough with them to give a good answer. My impression is that
+ they are good for parsing, but not as good for interpreting or generating machine code.
+ <a class="return" href="#af-5">return</a>
+ </li>
+ <li id="footnote-6">
+ Dart is an embedded scripting language, and statically typed, but it's not easy to embed.
+ There's zero documentation on how to do that, so you just have to rely on example code. But
+ you're gonna have to write your own compiler anyway, so it doesn't really matter that the
+ reference implementation is hard to embed. The hard part will be rewriting the Dart compiler.
+ <a class="return" href="#af-6">return</a>
+ </li>
+ <li id="footnote-7">
+ I hear a voice in my head screaming <strong><em>ASSEMBLYSCRIPT!!</em></strong> If I'm being
+ honest, AssemblyScript seems like a good language. The reasons I have for not recommending it
+ are that it's not 1.0, and
+ <a href="https://www.assemblyscript.org/standards-objections.html">the developers give me bad vibes</a>.
+ <a class="return" href="#af-7">return</a>
+ </li>
+ </ol>
+</footer>
+
+</body>