summaryrefslogtreecommitdiff
path: root/src/blog/best-tool-for-the-job.kuht
blob: ad89290254ac03f578012fa988c900dcf665cb84 (plain)
<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>