Elizafox's Ramblings

The blag/dumping ground of Elizabeth Ashford

I think about VM's a lot.

I don't mean the kind you run an OS in (a system virtual machine), I'm talking about process virtual machines.

Maybe you do too, or you wanna know more.

So, I'm going to dump the things I've discovered into a blog post, to help inform others how the sausage is made.

And yes, I have made VM's before. I have made both stack and register-based VM's. I'm actually thinking about making a JIT'ed VM, just to brush up my low-level platform skills.

Register vs stack-based VM's

For those who don't know, there are two main kinds of VM's: stack-based and register-based.

Stack-based VM's

Stack-based VM's manipulate a stack to store all data. It's not the most efficient way, but it works and is very simple to implement. CPython, PyPy, Perl, Ruby, CLR, PostScript, the JVM, and most others you'll encounter are stack-based.

Register-based VM's

Register-based VM's use registers to store data. They're not quite as common because they can be quite complex (more on that in a bit). A few register-based VM's you may know of are LLVM, BEAM (used by Elixir and Erlang), Dalvik, and Lua.

Side note

Many register-based VM's do have a stack to spill registers. Likewise, with a JIT, stack-based VM's often do register allocation. The difference can be quite muddied and sometimes not always so clear.

Personal opinion

You know, I really like register-based VM's in concept, but I can kinda see why register VM's aren't as popular.

I like registers much more than I like a stack. This is just a personal preference, but I've always found registers more easy to reason about than stacks.

Register-based VM's tend to be faster, because they're closer to how the machine actually works. However, they're harder to write a good JIT for.

JIT for a stack-based VM

A stack-based VM JIT is relatively straightforward. You can just “cache” hot stack elements in a register and operate on them, without having to repeatedly manipulate the stack. You know how many registers the hardware has, so you know how much you can cache. And when you need to spill back to the stack, it's relatively straightforward.

JIT for a register-based VM

This isn't so clear.

You often have thousands of registers to contend with, and you need to decide what needs to be spilled and what doesn't.

You get 6-7 registers on x86_32 to play with, so register allocation is obviously much more annoying on this platform. x86_64 has 16, and most RISC-y architectures have at least 32, so it's not as big of a problem on these.

Deciding where things should go on the actual hardware stack, what to keep in registers, etc. is not as straightforward. It's not like, intractable, it's just much more of an art (although optimal register allocation is an NP-complete problem, you don't need perfection, you just need it to be good enough).

Conclusion

I don't really have a conclusion for anything. I think stack-based and register-based VM's both have a place in this world. I think they're both neat! I have my biases, but I think that VM's are fascinating and I enjoy thinking and reasoning about them.

I hope I've learnt you some stuff about them too! 💜

If you have any questions, comments, corrections, etc. drop me a line on fedi. This blog is also available on Fedi at @elizafox@blog.elizafox.space.

— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay

Content Warning: politics; if you don't like politics, or wanna hear about them, you may wanna skip this one.

I've never really put pen to paper about my political beliefs in a single coherent document. I guess there's no time like the present.

WARNING: my beliefs do evolve over time. I can't promise that this will be authoritative in a month, let alone a year. This is merely a snapshot in time.

A rough overhead view from geostationary orbit

I am probably best described as a leftist. I believe in no true dogmas, but obtain my beliefs from a variety of sources. I believe there is no one source of truth.

I refuse labels like “Anarchist,” “Communist,” “Socialist,” etc., but if you really insist on putting me in a box, “Anarcho-Socialist” would probably be close.

Specific views

Though there is of course nuance to my beliefs, I will do my best to give a rough outline of what I believe here, based on economics, social equity, and governance.

Economics

I believe in equity for all. I also believe that achievement should be rewarded, but no reward should come from the backs of exploitation.

Those who cannot work do not deserve to live in penury. They are valuable members of the community and deserve our support.

Society is nowhere near full automation and we are absolutely reliant on labour. It's foolish to say we can abolish all work at this time. However, those who can work, should not be made to work beyond what they are capable of doing. If that means they only work a few hours a week, so be it.

We should endeavour to match people to jobs that use their talents to the best of their ability. Labour comes in many forms and does not necessarily mean that all work must be profit-motivated. A well-motivated amateur can outclass a jaded professional, every time. The tireless open-source contributor is as valuable as any professional.

Avoidance of burnout is paramount to maintaining productivity. Chasing endless productivity is a fruitless endeavour and at some point, productivity is waste. However, too little productivity does have negative consequences. Balance must be struck.

Working conditions should be as safe as possible. Safety must be job #1. Preventable deaths are a tragedy. There is no acceptable level of death in the name of productivity. Anyone who tells you otherwise should be made to work in a coal mine and then we'll see how they feel about safety.

Those who have too much should be taxed proportionally and help pay for improvements to society. I believe this is an imperative to ensure the survival and well-being of our species. Billionaires should not exist, and any philanthropic niche they fill should be filled by other means. We don't need Bill Gates to cure malaria, anything he can do, we can do.

Social equity

Bigotry of any kind, including racism, sexism, any kind of -ism, any kind of -phobia, is a blight and must be destroyed. The senseless belief in superiority over others is an illusion and is born out of inequality and scapegoating.

I believe everyone deserves to have their needs provided for, and some of their desires.

I don't think that means we should give everyone a toilet made out of solid gold. I also don't think we should give to those who already have or don't need it.

This means we should provide housing, healthcare (including mental healthcare), food, water, electricity, Internet, and basic entertainment.

I believe that rehabilitation services should be made more widely available for those who wish to (re-)enter the workforce. Few people truly wish to remain idle; often they're just stuck in a rut or need a helping hand.

We should work to help those who have trouble connecting with their communities to do so. Helping people make friends is the number one thing we can do to help with happiness and productivity. We are a social species, and we rely on each other to survive.

I believe governments should stay out of private lives unless harm is being committed. This means marriage should not be defined by the government, nor the shape a relationship should take, or anything of the sort.

I believe universal education, and the access thereof, is the key to a well-informed and critically thinking society. We must work to educate people in the way they learn best. Oftentimes, “learning disabilities” are simply the inability to learn based on outmoded and terrible methods that work poorly for many.

Governance

I'm still not sure how we can make an equitable government.

I don't think elections are the way forward, but instead appointing by lottery is the way to go. It may seem counterintuitive, but this selection process works for juries. If we do this for government, we can solve the cronyism problem.

I believe those who are appointed and turn out to be incompetent or corrupt should be able to be removed by popular vote, however.

I don't usually believe in hierarchies, but I also recognise the value in cooperation at multiple levels. I also recognise people often don't do very well without some kind of leadership and tend to wander aimlessly or bicker pointlessly without it. The question of how to ensure leadership doesn't become corrupt is an open one. I believe leadership should be as narrow and limited as possible, and only serve as a place where the buck stops and as a guidepost for where to go, based on the input of others. Leadership does not mean power vested in one person, however. It can be a committee or a group.

I believe in consensus-based governance. This doesn't mean that everyone agrees 100% on everything, just that a general consensus on how to proceed is achieved.

I do not believe in political parties or partisanship. People are multi-dimensional. Parties wind up representing narrow interests of a specific group and do not represent everyone.

I think the way “democracy” presently works is fundamentally flawed and stacks the deck in favour of the wealthy and powerful. Governments at present do not represent the will of the people, and oftentimes they make it transparent that they don't, in the name of some vague “greater good” that usually just means someone is making a buck. I also recognise that any form of elective democracy means the person with the most charisma will usually win, not the person with the most competence.

Conclusion

I hope I wrote my thoughts out coherently. I could write more, but I don't want to right now. I think I've written enough.

I hope this clarifies my positions on some things to people who were curious.

— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay

I've been using Rust for a month or so now, and I have some thoughts about it.

Diehard Rustaceans who believe their language is perfect in every way may wanna pass on this one.

Not everything I'm about to say is exactly nice, and I know how bigoted about Rust some of y'all are, and I know that any criticism of Ferris's Chosen Language™ is often swiftly met with rebukes and cries about memory safety and this and that.

Please understand, my criticism is not designed to undermine Rust! My criticism comes from a place of actually caring about the language. I want to see my gripes with it get fixed.

With all that out of the way...

The Good™

Rust does get a lot of things right.

  • Generics are awesome, as are generic traits, GAT's, and such
  • Traits rock; the ability to just extend any type is great
  • Love it or hate it, the borrowing mechanism is helpful in ensuring I never have to worry about use-after-free (unless I drop to unsafe... more on that in a moment)
  • Unsafe is a great escape hatch when you need it
  • The C FFI is pretty good
  • Default immutability makes you think about how you mutate data and side effects in your code, which is a good thing
  • Dependencies and packaging are mostly done right with Cargo, at least, better than I've seen most programming languages (looking at you, Python)
  • There is just enough access control to be useful, and do most useful tasks (like pub(super), pub(crate), pub, and the default of private); private being the default makes you think about what you export
  • Results > exceptions, full stop, this is how C++ should have done it
  • Clippy is one of the best static analysis tools I've ever used, bar none
  • rustc has great diagnostics, probably the best of any language I've ever used
  • Though some complain about it, I love the ? operator
  • The lack of function overloading; 99% of the time, when I wish I had overloading, what I really want is a generic
  • I actually like dyn a lot, the way it does trait-based polymorphism feels very natural to me
  • Concurrency in Rust is pretty good for the most part
  • The ability to easily export things to the C ABI is great
  • Having code work about as fast as the equivalent C/C++ code without much effort whilst being safe is fantastic
  • rustdoc is Fucking Awesome!
  • Rust encourages test-driven development

The Bad™

There's a lot I don't like, though.

  • Rust is a very complex language, more complex than C, and that's saying something (although it isn't a fractal of complexity like C++)
  • Why oh why do we need to explicitly upcast integers, the type system should not shit itself when an upcast is needed
  • Is your string a String, str, [u8], Vec<u8>, CString, OsString?
  • Conversion between string types is not straightforward
  • .as_str() vs to_str()... make up your fucking minds, which is it???
  • Interop with C often requires silly casts and is quite noisy
  • .into() either works or it doesn't, and you'll be left wondering why
  • If you need to build your own copy of the standard library (e.g. with opt-level = "z"), you need nightly
  • panic() should not unwind... why does it unwind wtf?
  • The C++ FFI story is pure 100% shit
  • Too much boilerplate for simple tasks like error handling
  • A lot of good stuff is in nightly only, with stabilisation seemingly not a priority
  • GUI is a horror show
  • Although not as prevalent as it once was, a lot of people are strapped to nightly at the hip, so you might be too
  • str.len() is the number of bytes in the string, which is rarely a useful thing to know, str.size() should be the number of bytes instead
  • Unicode manipulation in Rust is a sad story and you still need external libs; sad because they literally got it right by making strings UTF-8 by default, but gave you very few tools to do anything useful with them
  • Unless you use nightly, you have to repeat yourself with array type sizes: let arr[u8; 3] = [0u8; 3];
  • no_std sucks, a lot, it's borderline unusable outside of embedded and with support libs
  • OOM conditions always just abort, which may not be what you want
  • No default, optional, or keyword arguments
  • No native varargs
  • C varargs are somehow still unstable, so you can't write a C standard library on stable
  • You can't just turn an OsString into a CString, which to me sorta defeats the point of it
  • char is a scalar value, not a codepoint or character
  • If you need to add a lifetime to a struct, you then need to modify everything that touches that struct, including impls
  • As I've posted about before: no container traits
  • macro_rules! are ugly and complex, proc macros are even worse
  • The standard library is a little too minimalist, and depending on third-party crates isn't always fun or even feasible (makes auditing painful)
  • Lack of thread-safe data structures
  • Sometimes you'll randomly find certain things are bottlenecks that you don't expect
  • The borrow checker isn't always smart and doesn't always know when a borrow is safe
  • rustc's static analysis could be improved
  • Clippy should by default emit warnings about using unwrap() willy-nilly without a docstring
  • One real implementation means that the language is chained at the hip to LLVM's whims (yes I know about rustgcc, I look forward to being able to use it in 5-8 years)
  • In general, Rust may be at 1.70 at the time of this writing, but it feels like a language still in beta
  • Loading shared libraries at runtime is sadness and pain
  • The Rust governance structure is opaque
  • Sometimes what I want is the fastest integer data type on my architecture and don't care much about width because I'm not dealing with big numbers; closest you get is std::ffi::c_int, but that's still not quite right and unsatisfactory

The Ugly™

This stuff is really, truly, 100% unacceptable.

  • The Rust Evangelism Strike Force® and the attitude that Thou Shalt Rewrite Everything In Rust for questionable benefits; there is room for many languages in this world
  • The Rust community likes to pretend it invented programming safety; it did not, Lisp has been doing it since 1959
  • The Rust community gives me “Haskell before they looked in the mirror and did something about their toxicity” vibes, they pretend their shit doesn't stink, but trust me... it does
  • Editions are pointless, given you often still wind up depending on a specific rustc version or above
  • Unsafe code in Rust is more painful than it needs to be; lots of spooky undefined behaviour around it
  • If you write a lot of unsafe code (like in a kernel or such), Rust is honestly less ergonomic than C in many ways
  • Interior mutability is painful
  • Pointer provenance rules are largely undefined
  • Pointers in general need an overhaul
  • Stacked borrows with Miri are incredibly complex math-heavy things and aren't really how the language works, trying to understand it has given me migraines
  • Lack of cyclic garbage-collected structures makes implementing things like doubly-linked lists and trees that refer to their parents an absolute irritant without dropping to unsafe; I do know about rust-gc, but it's just not as good at something built-in
  • Speaking of that, I should not have to drop to unsafe to implement a linked list or tree
  • The standard library's linked lists suck, you can't even insert in the middle in O(1) time, without using cursors which are somehow still not stable
  • Rust's evolution is basically corporate-driven, not community-driven
  • SO MANY FUCKING CRYPTO GRIFTER COMPANIES IN THE RUST COMMUNITY, burn them, burn them all to the fucking ground

Conclusion

I still love Rust. I could write a huge list about things I love and hate in C, too. However, my Gods, it could be so much better than it is.

I want to see it get better over time.

I doubt I'm the first to have noticed all these good, bad, and ugly things, certainly.

If you have a constructive argument for or against anything here, let me know what you think on Fedi. I welcome all constructive feedback. 💜

— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay

I'm back in Seattle but I got shit to do. This is gonna be a short post.

As per my last post, I was experimenting with doing mangled import from Rust.

I now realise how bad of an idea that is.

Metadata

Every dylib contains metadata that helps elucidate the types of functions, struct layout, MIR code of generics, etc.

Unfortunately, parsing said metadata seems to be a nightmare, and pretty much only the Rust compiler knows how to do it.

Allocators

Turns out Rust dylibs may or may not need an allocator, and that has to be set up if you load something.

Unloading

Unloading a Rust dylib that isn't expecting to be unloaded means you're likely to have a bad time, as things get dropped that aren't expecting to be dropped.

Too many symbols

Rust exports basically everything into a Rust dylib, it doesn't really try to hide things or pare them down. This is pretty awful practice. It pollutes the namespace.

Unstable ABI

The Rust ABI is unstable, and subject to change at any time. Just because names are mangled more or less consistently, doesn't mean that you can just willy-nilly import things. There is more to the ABI than that. Said metadata is part of it as well (the .rustc section of a dylib).

Sigh

I hope one day this becomes a possibility, because this would be a big win.

In the meantime, I recommend checking out the abi_stable family of crates, which can help you include rich Rust in your crates, instead of just exporting a C ABI and making a cdylib.

— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay

I'm writing a mangled symbol loader for Rust.

I'm actually soliciting feedback from experienced Rust programmers here, which would be greatly appreciated 💜.

General design

I use a design similar to Boost.DLL's mangled import.

I scan the object file for exported symbols. On Mach-O (macOS and friends), I scan the relevant architecture.

While scanning the relevant exported symbol table, I use rustc-demangle to demangle the symbols. I create a map of mangled symbols to demangled symbols (Note: on Mach-O, I also strip the leading underscore, because macOS and friends prepend an underscore to all symbols in the file).

Once I demangle the symbols, I can then use libc::dlsym or whatever to load a symbol from a string, after looking it up in the map.

Drawbacks

Obviously there are caveats I am aware of.

  • Rust has no stable ABI; this approach is not guaranteed to work between compiler versions, although I trust rustc-demangle will be updated promptly. Nonetheless, everything must be built with the same compiler. I think this would be a problem with just linking with a Rust library in general, unless some sort of backwards compat stuff is in there that I'm not aware of. I should use said mechanism if it exists; let me know if it does! Update: I found out there is in fact metadata I can use.
  • A lot of symbols are exported in a Rust dylib; I don't know which ones are relevant to the functionality of the shared object, or if it's not as simple as just “demangle, map to a mangled symbol, dlsym and you're off to the races.” But so far it all seems to work, based on limited testing.
  • Due to the above issues, I worry this system is brittle. I also question if Boost.DLL isn't also brittle, because ABI has been broken before by GCC and Clang in C++ (they're not even 100% compatible with each other afaik).
  • I don't do parameter checking during the mapping process; in fact, I strip off the hash, which could be used (I think) in theory to do such checks. I haven't implemented a scheme to actually generate this hash, and I don't want to maintain something like this (it would involve parsing Rust code, and I don't want to write a Rust parser). This makes mangled imports unsafe. But this is a problem with importing unmangled functions too, and there's not much we can do about it.
  • The loading process on every platform I'm aware of involves a cast from void* to an extern “Rust” function pointer. Guaranteed to work on Unix by POSIX, and I think Windows (but I'm not 100% sure on that). But I don't know the applicability on things like WASM or AVR. Then again... are shared/runtime loadable objects even a thing on such platforms???

Comments welcome

Let me know what you think on Fedi! I welcome all constructive feedback. I'm sure I haven't thought of everything, and I really want to know what you think. 💜

I do ask that you please bring well-thought out arguments, and not just ad hominems and parroted, cargo culted nonsense.

— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay

This is gonna be a short post. I'm tired. I'm in God damned Twin Falls, Idaho. I'm a day away from home.

(Author's note: guess I blew the whole “short” thing, because of course I did)

I want to talk about something Python does bettet than Rust, even whilst not having a 1:1 mapping of the trait concept.

Assumptions

I assume you know what a trait in Rust is; if not, go find out (or by example if you prefer).

I also assume you know what an Abstract Base Class — an ABC — in Python is. If not, here's some quick information.

With that out of the way...

My gripe

The ABC concept doesn't 1:1 map to traits, but there are similarities.

Notably, I want to point to the fact Python's collections have ABC's, but Rust has no base traits for its containers.

Why is this a problem?

A unified interface

In Rust, traits more or less specify an interface.

None of the standard containers implement container traits.

This means there is no unified container interface.

This may not seem like a problem, but it makes for some annoyances.

Genericity in accepted containers

Consider you want to implement something in Rust that takes any mapping container at all.

This is pseudocode-ish, but you should get the idea. The idea here is we extend all mappings with a new method:

trait dyn Map {
    fn replace(&mut self, key: Self::Key, value: Self::Value) {
        self.remove(key);
        self.insert(key, value);
    }
}

Obviously, such a trait like Map would be super useful in this case.

But it doesn't exist!

You have to roll your own (example stolen from Reddit):

use std::collections::{HashMap, BTreeMap};
use std::hash::Hash;

trait MapExtend {
    type Key;
    type Value;
    fn replace(&mut self, k: Self::Key, v: Self::Value);
}

impl<K: Eq + Hash, V> MapExtend for HashMap<K, V> {
    type Key = K;
    type Value = V;
    fn replace(&self, k: Self::Key, v: Self::Value) {
        self.remove(k);
        self.insert(k, v);
    }
}

impl<K: Ord, V> MapExtend for BTreeMap<K, V> {
    type Key = K;
    type Value = V;
    fn replace(&self, k: Self::Key, v: Self::Value) {
        self.remove(k);
        self.insert(k, v);
    }
}

fn main() {
    let mut my_hashmap: HashMap<i32, f32> = HashMap::new();
    my_hashmap.insert(42, 23);
    my_hashmap.replace(42, 69);
    println!("{}", my_hashmap.get(42).unwrap());

    let mut my_btreemap: BTreeMap<i32, f32> = BTreeMap::new();
    my_btreemap.insert(420, 31337);
    my_btreemap.replace(420, 1337);
    println!("{}", my_btreemap.get(420).unwrap());

But this sucks! It means that all containers now have to be bent to work with your funky little trait, rather than there be an official interface. It isn't difficult... but it shouldn't be like this.

And of course, it means tons of boilerplate.

Python's ABC's

As for what Python can do here; you could monkeypatch the relevant ABC (although I don't know if it would affect built-in containers... there's a lot of magick in the system I don't quite understand), but you can also subclass containers as needed.

You can also check if a container matches a type, like so:

from collections.abc import MutableSequence

def rotate_seq_right(seq):
    if not isinstance(seq, MutableSequence):
        raise TypeError("not a MutableSequence")

    elem = seq.pop(0)
    seq.append(elem)

And this will work with any mutable sequence, and give you a diagnostic if your type isn't one.

How did Python, a duck-typed language, do better than Rust here???

Simplified container implementation

In Python, ABC's specify what a container implements. If you inherit from the Mapping ABC, your container is a Mapping, full stop.

This means that extra functionality can be had for free just by inheriting from an ABC, which greatly simplifies container implementations. You can implement any type of tree, any type of mapping at all, you could even make a keyed linked list if you wanted (don't do this, please). By implementing a few methods, your type is first-class with Dict, which is also a mapping.

Instead, you have to implement it all by hand and hope it conforms to the interface users are expecting. Fun!!!

Conclusion

Rust, please implement container traits? Please? It would be super useful and helpful and it would enable everyone to extend all containers easily as well as other funky shenanigans like passing containers as generics without specifying the container.

Pretty please? 🥺🙏

— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay

I've made no secret for a long time I have many diagnoses. EUPD, MPD, C-PTSD, Autism/ADHD, DID, those are the big demons.

For too long, it's been a crutch I've leaned upon. I ascribed everything I did wrong to it.

What exactly did I do wrong? I acted out, I made people anxious, I tried to get attention in bad ways, I sometimes overstepped boundaries.

And it was wrong of me to do so. I can only apologise and hold myself accountable, and allow others to do the same.

I will share what's helped me on my journey. I'm ridiculously far from perfect or where I need to be, but I know where to start now. I hope I can help someone along their path.

Ultimately, you must find your own path. All I can share is the path I took, if you wish to follow it. Deviate where you must, but never give up.

Change is hard, but possible

At the end of the day, the only one who can change you, is you. No one else can do it. Not even the best therapist in the world can do it.

A therapist can teach you how to manage your demons, but it's your job, solely, to do so.

Holding yourself accountable is part of that job. Apologising and working hard to change is part of that job too.

It's not easy to change. Old habits die hard. Behaviours which may have been an adaptation to a bad environment take time to unlearn.

Love yourself with all your heart, even if you feel unlovable

Throughout all this, it's important to love yourself. Even if you feel unlovable. Even if you feel like a piece of shit.

Maybe you've hurt people. Most people have. Maybe you've been a piece of shit. At some point, most people have been too. Maybe you've acted out. Most people have done that as well. Worse people have walked the Earth than you, believe me. Much worse.

Don't hold yourself to a standard you wouldn't hold others.

Don't throw yourself a pity party. Do something about it. Check yourself. Learn how to analyse your emotions. Learn how to be mindful. Tell yourself that you're good enough. That doesn't mean you can't be better, but you are sufficient. Even if you don't feel good enough, do it anyway. Remind yourself that you will make mistakes.

Wallowing in sadness achieves nothing. Working hard on yourself does.

Change comes from within. If you truly want to change, don't blame others, but also stop blaming yourself. Blame is not constructive. Recognise your faults as if you're investigating yourself and looking to fix them. It's a problem solving exercise, one which if you're truly dedicated, you'll eventually solve.

To err is to be human

You will fuck up again. You are not perfect. None of us are. You may even make the same mistake repeatedly until you learn. Don't let that stop you.

Hold yourself accountable for your fuckups, and then think, “how will I try to prevent this from happening again?” Analyse your thought processes. Analyse your reasoning. Go over everything with a fine-tooth comb. Think about what needs changing, and integrate it into your routine.

Yes you can

If you hold yourself accountable and work to change your behaviour by being mindful, and recognising when you fuck up, you will eventually change those behaviours. Perfection isn't the goal, improvement is.

Cast out the negativity

Ditch anyone who expects perfection. Ditch anyone holding you to to impossible standards. Ditch anyone who says you can't change. Get rid of the negative people in your life and those who don't really want to see you get better. They will only drag you down. They will not help you grow.

Nothing is worse than a person who wants to hold you back.

You must remove the trash from the garden before flowers will grow.

Better late than never

Improvement and learning is a lifelong process. Change won't happen overnight. It will take time.

But no matter if you're 8 or 80, change is always possible.

You don't stop learning just because you get older. Sure, habits become more difficult to break, but it's never impossible.

If you want to change, deep in your heart, do it now.

You are not alone

Most people have some friends. Find yourself good ones. Surround yourself with those who build you up and are invested in seeing you get better.

If you can't afford or find a therapist (therapists are often hard to find in some places; we are in the midst of a mental health crisis), look up things like Cognitive Behavioural Therapy and Dialectical Behavioural Therapy techniques. It's not perfect, it's not as helpful as a therapist, but it's better than doing nothing.

Don't let the lack of a therapist make you think you can't change.

And don't let backsliding stop you. What matters is you're trending upwards. You may have huge dips in the road, but you need to work past them.

Failure is NOT an option

Don't give up. Never give up, no matter what, no matter how hopeless it seems.

Nothing in your life is unfixable. People have gotten out of worse situations than you.

You can do this. I believe in you. 💜

— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay

I was talking with my friend Anna about runtime modularity in Rust.

It turns out, unless you export a C API, or are willing to wade through mangled symbols, there's no real ability to do runtime modularity.

This is incredibly depressing and a sorry state of affairs.

It's also one that is, sadly, rather typical of modern languages.

The case for runtime modularity

Loading modules at runtime is incredibly useful. It lets us hotpatch functions, configure things and load functionality at runtime, and more.

IRC daemons do this as a standard practise since the dawn of time. Loading modules is integral to how a modern IRC daemon works.

Apache uses it to load modules at runtime. That way, you don't have to bloat your server with modules you'll never use, but they're always there if you need them, without recompiling. It's nifty!

So if something like this is so fundamental, why isn't it more widely supported?

The scourge of C

Because of name mangling, it's a complete pain to actually load things at runtime that aren't C symbols. C symbols aren't really mangled, per se (but see this and this), so loading them at runtime is fairly simple. As the article on name mangling says, we need mangling to actually express modern concepts such as monomorphisation and function overloading and such.

Unfortunately, pretty much no language makes it easy to mangle a symbol, especially at runtime. This is needed to use dlsym() or GetProcAddress() to load the symbol so we can use it.

Rust can't do it. C++ can't do it. D (to my knowledge) can't do it.

It's infuriating because the compiler knows how to do it. Yet it won't tell you how!!! Why, is beyond me.

The reason we have to do this name mangling crap to begin with is because of C. C hangs over all our heads as the lingua franca of all our systems. We must bend ourselves to its evil whims to accomplish runtime loading, because there is literally no other way.

Which means that we're all forced to speak C, or use horrible unportable hacks like reading the entire library's symbol table and demangling it.

Boost.DLL is one such package that does such cursed things. It can do a mangled import from a dynamic library by scanning the symbol table and demangling, at least on some platforms. Just don't look at how it's implemented. Unfortunately, such cursed things are not generally available for most languages, and if your compiler or OS isn't supported by Boost.DLL, too bad.

Why it matters

The fact anyone not using C has to either resort to cursed hacks or basically export all their symbols as C-style symbols with either bespoke mangling or no mangling means that we all wind up suffering with C's limitations.

It means we usually get no namespacing unless we do it ourselves. It means we can't hide our functions from C (which may be desirable, especially if we throw exceptions, because unwinding into C is a horrible idea). It means we have to contort ourselves into the C way of doing things, instead of expressing ourselves in our language's native tongue.

There's a spectre haunting programming. The spectre of C. I wish we could do away with it.

So... why do so few seem to care?

🤷‍♀️

I figured runtime modularity would be a killer feature everyone would want, especially a way to do it in a natural way for the language, instead of dropping to C or changing how our functions are exported.

But it seems not.

And it's saddening.

We should be able to do this! Something as simple as runtime modularity, which is far from a new concept, shouldn't be so hard or so hacky.

And what would fix this?

A better ABI where we can express our language's concepts would be helpful but since that won't ever happen...

Literally exporting a way to mangle a name from a path.

It isn't a perfect solution, but it would be a solution. It would be better than just trying to pretend we're speaking C.

I implore programming language authors, library authors, anyone, please please please, I beg you, export your name mangling APIs in an accessible way.

— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay

Apparently changing your avatar is still not a supported thing here without editing the image manually on the filesystem. -_-

Grumble.

Well, whatever I guess, it is ActivityPub software, I should be grateful it works at all.

— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay

I'll probably jot down my thoughts that are too long for my main account.

This thing also speaks ActivityPub apparently? So people on Fedi can also follow my blog. Super cool!

I will try to remember to use this for my big posts, and make a post back from Fedi to here when I make a new post.

Stay safe and healthy. 💜

— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay