Blog TODO#
- OOP polymorphism post
- Post to this-week-in-Rust
emplace
isn’t as powerful as you think- https://news.ycombinator.com/item?id=33705661
- Just show it in Godbolt
- Things they didn’t teach me at university
- Different states of code
- Is it code complete?
- Is it merged?
- Is it released?
- Is it tested? What tests?
- What versions is the feature in?
- What is a breaking change?
- How to git?
- Why do you merge like that?
- There’s two merges
- trunk into feature branch
- feature branch into trunk
- Why?
- Merge conflicts should be resolved in feature branch
- Merged version should be tested
- There’s two merges
- Git is super complicated
- XKCD link
- Why do you merge like that?
- Different states of code
- Book of C++ vs Rust
- Blog post about Rust in the Linux kernel
- Write simple simple Reflex example
- First two of the 7 GUIs
- Or use simple one from reflex-examples
- Follow up reflex-examples repo
- “Words committed” per month tool
- Rotate through categories and books
Books TODO#
- Finish C++ book
- Just rotate between them and keep writing
- Individual TODOs in individual repos
- Find better names
- Figure out pitch strategy
- Include public books on website
- Figure out how to add Google Analytics
- Figure out licensing/contributions
- Move “How to Run a Programming Shop” from secret into not-secret
- Blog post announcing books
- “Gardens and streams” outline
- Link books page
- With non-secret books only?
- Also obvs link other garden-like stuff
Blog Outlines#
Rust#
- Programming languages as fandoms
- Rust vs Python experience
- I don’t program in dynamically typed langauges very often
- Rust makes you say a lot more things explicitly
- Disadvantage: You have to understand all those things
- But it helps you get them right
- Including many things that are run-time errors in Python
- Advantage: You can let the compiler do a lot of the work for you
- Just change the part you want to change
- And then let the compiler errors form a TODO list
- Writing error-free code the first time is not a goal
- If you make it a goal, it will be too tedious
- New code versus maintaining code
- Writing new code in Python is easier
- Making changes is harder
- You generally have to change code in a bunch of corresponding places
- The Rust compiler will find them for you!
- It’s OK to rely on this, that’s part of its job
- Some extreme examples of type safety are to help programmers rely on this
- e.g. bunch of newtypes for integers, this is why
- Similar to keyword arguments
- But checked ahead of time
- Rust safety, again
- Criticisms of Rust safety mechanism
- Comments on https://lwn.net/Articles/907685/
- Safety is circular
- Unsafe is a hole in the proof
- Memory leaks changed status
- Find hacker news post
- Responds to a naive concept of what safety is
- Actual safety is nuanced
- Responds to an oversimplified concept of what a “safe” PL is
- Not quite a straw man
- Since Rust fans sometimes misunderstand in the same way
- Or promote the oversimplification
- Rust detractors maliciously promote it
- And refuse to learn more, which would disillusion them about the goals
- The straw man concept of a “safe” PL is incompatible with a systems PL
- But just because Rust can’t achieve it, doesn’t mean Rust is useless
- Because Rust gives you tools to manage potentially memory-corrupting features
- Whereas C++ is just a dumpster fire
- So what IS Rust’s deal with safety?
- Compilers provide ways of proving things about your code
- C++ does this too
- Most properties are IF you don’t do X, THEN you will have this property Y
- The IFs for C++ are ridiculous
- The Rust ones are more contained
- Rust concept of safety is list of properties it can prove
- As long as you don’t use unsafe, OR
- As long as you follow VERY strict rules with unsafe
- Yes, the definition of “safe” is defined by the PL
- It’s the list of things you can prove Rust won’t do!
- It doesn’t correspond exactly with “not crashing”
- It does correspond well with “no memory corruption”
- It is a very useful set of guarantees
- Compilers provide ways of proving things about your code
- Criticisms of Rust safety mechanism
- Async and threading explanation
- Threading:
- Multiple cooks
- Implementation
- Multiple per CPU
- Single per CPU
- Cook per task
- Meeseeks
- Thread pools
- Async:
- Single cook
- Has to keep track of what they’re doing
- Lots of post-its
- Threading:
- Garden idea: Errors I hate
- Presence or absence
- Async recursive errors
- No error for binding unit
- Underexplained error
- File not found
- But why?
- Which file?
- OS errors often don’t include this
- C/POSIX errors are single integer
- OS errors often don’t include this
- What were we trying to do?
- Mitigations
- map_err
- wrap_err
- File not found
- How serde works
- Generic, monomorphized programming with the policy pattern
- Case study:
serde_json
- serde dbus
- A flaw in serde
- zbus’s solution
- Unwraped Extra: Unchecked Assertions for Performance
- Benefits of undefined behavior from C++, if wanted
debug_unwrap
could exist- Would potentially eliminate unconditional checks backwards
- Found out someone wrote it:
unsafe_unwrap
crate
- New Series: Long-form explanation of how to implement a single function or class
- https://docs.rs/tokio/latest/tokio/io/fn.split.html
- std::function
- Link previous post
- Demonstrate IntFn implementation
- Demonstrate IntFn implementation using hack
- Look into how std::function is in fact implemented
- hobby/llvm-project/libcxx
- Try to retrofit hack
- Compare actual performance
- crossbeam write up
- std::thread::spawn takes a ‘static closure
- It has to!
- Even though you might immediately call
join
- Because they’re separate calls with no connection
- crossbeam connects them
- And uses
unsafe
internally to do so!
- And uses
- std::thread::spawn takes a ‘static closure
- The policy pattern
- retry policy example
- come up with a second example
- maybe string formatting for streaming data
- implementations:
- booleans
- bad
- enums
- OOP-style interface or inheritance-based polymorphism
- FP-style: use functions
- trait-based polymorphism
- dyn, the Rust analogue of the OOP way
- impl Trait, which compiles away
- booleans
- serde as a huge case study
- GC’d Rust converges to Haskell
- See: https://boats.gitlab.io/blog/post/notes-on-a-smaller-rust/
- Memory as a resource is too low-level for most people
- Long live GC!
- Lifetimes is fundamentally tied up with call stacks
- A low level concept
- Mutation, and how Rust tracks it
- Is similar to low level memory management mechanisms
- We can mutate when we don’t have references to old version
- Similarly, we can drop
- Is similar to low level memory management mechanisms
- Haskell has mutation all over the place
- More literally (ST monad)
- Less literally (Reflex’s
Dynamic
)
Haskell#
- Why Reflex? Blog post
- Write simple example set in Reflex
- How Reflex? Blog post
- Walk through setting up examples repo
- Walk through a more complicated example
- Chess
- Why callbacks are bad
- Reflex resources page
- Haskell: Simpler typeclass than functor
- Typeclasses series:
- Type class 101
- Monoid
- Functor
- Applicative
- Monad
- Typeclasses series:
- Implementing Wordle in Reflex
Other#
- Internet corporatization
- PL doesn’t matter, subject matter does
- Thoughts
- One-off scripts don’t require error handling
- I didn’t know what data frames are
- Thoughts
- The ironic way that bugs work
- The more bugs you fix, the more bugs you’re likely to have to fix
- Why we leave code crappy?
- A counterpoint to “if it ain’t broke, don’t fix it”
- Sometimes, it’s too dangerous to fix
- This means there’s insufficient tests
- Sometimes, it’s too expensive to fix
- People don’t factor in the technical debt often here
- Hotspot mode-based connection
- Last look vs not last look
- Write garden/stream blog post
- Facebook, Twitter… Wikipedia
- Is Wikipedia a social network?
- Kind of!
- There’s several reasons Wikipedia is less trashy
- Non-profit
- Only goes so far
- Focussed on topics
- Not individuals
- Shares with Reddit
- Posts live forever
- And improve with time!
- Non-profit
- Is Wikipedia a social network?
- Garden vs stream
- Basic concept
- Cite sources
- Give examples
- Benefits
- Encourages revision
- Allows bigger things to be built
- Allows organization
- Facebook, Twitter… Wikipedia
- Row vs column oriented (enums and Dynamics)
- Basic concept
- Smalltalk code browser
- Supported typical “all methods in a class”
- But also “all receivers of this message”
- Neither felt like the “real” way to organize
- Because the code wasn’t in files
- Very easy to add new methods to existing classes
- If we had a new class-based decision to make
- Example inheritance hierarchy
- A bytecode-esque representation for a simple DSL
- List of things to do, can take various shapes
- Runtime heterogeneous list, seems perfect for polymorphism
- Enums/switch vs inheritance
- Silly small inheritance hierarchies
- And adding dozens of little methods
- Disrupt the flow of where the program is going
- Java
instanceof
- Rust
enum
s
- Silly small inheritance hierarchies
- Reflex Dynamics/foldDyn
- FRP blog post on Reflex
- Compare to callbacks
- How the code is organized:
- “These are the things that happen when you click this button”
- “These are the things that are allowed to change this value”
- Sample program that has buttons that do something to some numbers
- vs callbacks that update shared state directly
- when event X happens, Y value is updated
- sort by events or updated values?
- FRP blog post on Reflex
- Binary has no intrinsic meaning
- In elementary school, people asked if I could “read binary”
- Pop culture messages in binary
- Usually encoded as ASCII
- “Science, bitches”
- But of course, ASCII text is not the only encoding
- Comparison
- Asking if you can read binary is like asking if you can read “Roman letters”
- But those can be used for German instead
- The more relevant question is, can you read English?
- Roman letters is analogous to binary
- But language is analogous to “encoding”
- Asking if you can read binary is like asking if you can read “Roman letters”
- Example
- Do out “Hi!” as string (including terminating null)
- Write out in endian-neutral fashion, one line per byte
- List out interpretations rapid-fire
- Interpreted as u32
- Interpreted as i32
- Interpreted as C string
- Interpreted as f32
- Other interpretations, in context?
- How do we assign what binary gets what meaning?
- What operations are run on it.
- PL’s responsibility to run right ones
- Different PLs use different book-keeping
- Rust and C++ do their book-keeping at compile time
- Demonstrate with Rust example code
unsafe
means “allow you to override book-keeping”
- Demonstrate with C++ example code
reinterpret_cast
means “book-keeping: new interpretation”
- These book-keeping systems are called “type systems”
- Do out “Hi!” as string (including terminating null)
- In elementary school, people asked if I could “read binary”
- The history of flow control
- Batch jobs vs interactive CLI vs event-based programming
- vs Reflex!
- Batch jobs vs interactive CLI vs event-based programming
- What’s slow and what’s fast
- Other Tower-book based blog posts
- Fail fast, fail often
- Why error messages should still signal even if the compiler “knows what you mean”
- The Specialness of the Number Zero
- Zero is unnatural for humans
- Pick up all the cards on the table
- Human: “There are none.”
- Computer: “It is done.”
- All immortal humans have three eyes.
- Is this true? False? Not even true or false?
- Mathematician: True!
- Pick up all the cards on the table
- Math
- No elements in sums
- n * 0 = 0
- Products
- n ** 0 = 1
- No elements in sums
- Programming equivalent
foldl
sum
,product
foldl1
- Outside of math:
- Need an identity
- AND –> true
- OR –> false
- Regular expressions
- | does OR
- But… "" matches everything, not nothing
- Zero is unnatural for humans
- Code is not a matter of personal taste
- Convention is actually a good thing
- Be conventional when you can
- Convention is actually a good thing
- Alternatives to traditional call stacks
- Stacks are an assumption of C/C++
- Often glossed over
- Supported directly in hardware (Intel)
- And ABIs (which assume C)
- Investigate C++ co-routines
- Cite “state machines” section in Hubris and Humility post:
- Credit John Ericson
- C is not “portable assembly”
- Normal assembly allows you to control the stack
- Operating system implementation of stacks
- Processor implementation of stacks
- Sometimes, stacks don’t work right
- Upward/downward funarg
- Smalltalk
- GC’d linked list
- With invalidated “return” pointers
- Co-routines
- Async
- Upward/downward funarg
- Stacks are an assumption of C/C++
- HTML4 vs HTML5 spec
- HTML5 spec had a much broader scope
- Included parsing
- Included complete parsing, including how to fail
- This was a source of incompatibilities in practice
- Why was this important?
- Authors were unwilling to migrate to XHTML
- Especially with XML mime types
- HTML4 could have mandated complete failure for malformed HTML
- This would have maybe worked, maybe not
- Reigning in the Internet wild west would have been difficult
- There was already lots of precedent for ignoring the spec
- Authors were unwilling to migrate to XHTML
- This was a codification of HTML as a practice
- And a standard for implementations to deal with it
- Included quirks mode!
- Relevance
- What does this mean?
- What does this mean for Rust?
- Formal specs often exist in an alternate universe
- Influenced by mathematics
- Prioritizes:
- Rigor
- It’s either valid or invalid
- This is more commonly a spectrum
- Link previous related essay
- Conceptual clarity
- There is one right way to do each thing
- Rigor
- These things are good things overall
- Appreciate that Rust enforces safety
- But a bad match for the web
- Which had already developed a wild west culture
- HTML5 spec had a much broader scope
TODO in order:
- Binary has no intrinsic meaning
- Begin Reflex tutorial
- Fix reflex-examples CI
- C++ Post
- Pro-RAII
- Pro-template metaprogramming
- Finish Wordle
- Add to reflex-examples
- Add unit tests to chess app
- Garden related idea:
- RFCs/Rust proposals I’m a fan of
- Errors I hate
- Opinions I have
- Script to publish posts
- Two repos, one public one private
- Script verifies we’re on master or main branch
- Script verifies we’re pushed up to date on private repo
- Script refuses to do a force-push of public repo
- User must manually resolve
- Make sure stale files are not left in place
- Use renames?
- Try to fix error from Reflex error post:
- Reflex frontend “book”
- serde_dbus/zbus post
- Unwrap post
- Rows vs. columns
- Why
dyn
is the way it is post (short) - Choose a section and write it from book
- Not exactly, make it feel more like a blog post
- Java-style getters and setters are dumb?
- serde discussion
- with serde_dbus
- Pitch books
- Write pitch for “Beyond OOP”
- Write pitch for “Organization of Memory”
Other:
- ADHD-friendlier git
- What was this??
- Write up of this feature: https://rust-lang.github.io/rfcs/3058-try-trait-v2.html
- Write about optimizaion
- What’s slow and what’s fast
- Configure time vs run time, what’s in a tight loop
- Why C++
std::unique_ptr
and Rust’sBox
- Why is unique_ptr or Box used instead of by-value?
- Benefits of indirection besides aliasing
- Variable size
- Inheritance in C++
- Pointer can point to different parts of same object
- !Sized in Rust
- Including
dyn
, Rust’s inheritance analogue - Pointer can be fat
- Including
- Inheritance in C++
- Copies (or even just moves) can be expensive
- Large type
- Manages large type
- Passing up the call stack
- Variable size
- Downsides of indirection
- Slower
- More operations
- Both dereferences and alloc/free
- Cache incoherency
- Memory fragmentation
- More operations
- Slower
- Benefits of indirection besides aliasing
- Why is unique_ptr nullable?
- Let’s try to write a non-nullable moveable pointer
- Why is unique_ptr or Box used instead of by-value?
- C++ Indirection: Use Raw Pointers Rarely
- See: https://www.reddit.com/r/cpp/comments/uwrpb1/class_construct_arg_lifetimeownership_assumptions/
- Raw pointers are the most primitive expression of indirection in C++
- Direct representation of a processor-level concept
- No additional information on top of that
- Too many possibilities, is confusing
- Let’s talk about all the use cases for them
- And alternatives
- As non-owning aliases
- Use references
- References can only do this
- Makes ownership semantics clearer
- What about optional references?
- Pointers are nullable
- I basically used to use pointers for this
- But you can use
std::optional<T&>
- Use references
- As owning handles
- Use
std::unique_ptr
- Use
- As handles for multiple T
new[]
/delete[]
- Any reason not to use
std::vector
instead- Um, OK, use
std::unique_ptr<T[]>
…
- Um, OK, use
- As iterators into some array
- If you use
std::array
orstd::vector
, you can use real iterators
- If you use
- As C API compatibility
- This kind of needs to be raw pointers
- Interfaces with C only
- As implementation detail of
std
typestd::unique_ptr
at the least needs pointers internallystd::shared_ptr
,std::vector
, etc easier to write with pointers
- C++ vs Rust pointer comparison
- Properties of a pointer
- Nullability
- Ownership (lots of options here)
- Rust pointers
- C++ pointers
- Properties of a pointer
- Async errors post
- Intro
- Async is great
- Polish
- Errors
- e.g. Blocking function example
- Function colors are great
- No errors for calling blocking function is bad
- Even FasterThanLime gets tripped up
- Function colors are great
- Recursive example
- Recursive functions are impossible in async
- Why? Stackless
- But these errors are ridiculous
- Reduced example of
watchman
at 8515c475c
- Reduced example of
- Recursive functions are impossible in async
- e.g. Blocking function example
- Sample program (TheCodedMessage/static/async-example)
- nc based command processor
- two commands: echo, and uppercase
- add new command, listen, which adds another listening port
- implement naively and recursively
- how to fix?
- Intro
- Async problems in Rust
- Recursive async functions makes no sense
- Recursive with normal Box still does not compile
- Error message is redonkulous
- Construct minimal example from ~/racepoint/watchman
- Pre current commit
- Construct minimal example from ~/racepoint/watchman
- Write about this:
- C++ vs Rust: Signed and unsigned arithmetic
- Nullability is actually bad
- Musing on Results/enums
- Problem statement (mostly done)
- Techniques to handle it:
- Special error codes (C)
- Numbers/values that just won’t come up
ssize_t
and-1
- Taking advantage of nullability
- Can interact poorly with other exceptional conditions
- Error vs “not found”
- Objective-C implicit nil bubbling
- Out-of-band mechanisms to indicate what error
errno
- Case-by-case
- Requires lots of reading docs
- Numbers/values that just won’t come up
- Returning multiple values
- C/C++ out parameters
- Return value is success/failure
- So if statement can wrap around function call!
- Because we want to branch on it
- Also, so erroring can short-circuit inside implementation
- So if statement can wrap around function call!
- Out parameter is desired information
- Return value is success/failure
- Can just forget to check
- Go (_, err) tuples
- Basically equivalent
- C/C++ out parameters
- Exceptions
- Checked exceptions
- Java
- C++ attempt
- Unchecked exceptions
- Really hard to code around
- Entire books on C++ exception-safety
- Perform really poorly
- Doesn’t handle “unexceptional” cases
- Poor performance for them
- Like finishing a loop
- Or “didn’t find the thing”
- In any case, banned in many C++ platforms
- Closer to Rust panic than Rust
Result
- … which brings us to …
- Checked exceptions
- Algebraic Data Types
- Haskell
- Rust
- Handles other situations
- “Not found”
enum
s also handle queries- Where often we want to do something, depending
- Iterator/loops
- “Unexceptional exception” for loop end
- Similar to return enum
- Discuss type-state based iterator
- slight side topic
- maybe a separate blog post
- next consumes
- gives you either a new iterator and a value
- or nothing – you’re done!
- Propagation operator
#[must_use]
- Almost never treated as a value
- Except to propagate upward
- Ignoring it is almost certainly a bug
- The
match
(or?
) and the return type- … seem intertwined
- Almost never treated as a value
- Continuation passing
- NodeJS
- Lisp
- Equivalence of returning an
enum
- With having different return continuations
- Compiler representation
- Theoretical ABI
- Credit MenuetOS
- Credit John Ericson
- What ABI does Rust, in fact, use?
- Special error codes (C)
- Subroutine history
- And abstraction generally
- Do something with that draft
- Knight Capital disaster
- What if BeOS were open source?
- https://www.howtogeek.com/696193/what-was-beos-and-why-did-people-love-it/
- What is BeOS?
- BeOS in programmer culture
- Never used it myself
- Cost money, was a teen
- (Do some research into timelines)
- Read and treasured the BeOS Bible
- Cost money, was a teen
- Almost became macOS
- But NeXTStep stepped in instead
- Common fantasy among Be fans
- Palm bought Be
- But what if they’d open sourced it?
- Haiku is struggling
- Lost the technical edge with the gargantuan task of re-impl
- There could be a viable OS desktop operating system
- A true workstation Unix
- Linux on the desktop is a shit show
- Linux is a great server operating system!
- Linux desktop users
- Out of touch
- Trying to get the kernel to prioritize the desktop
- They don’t want to
- macOS has corporate issues
- Linux on the desktop is a shit show
- Copyright policy
- Source code should have to be registered
- If you’re not using it commercially, you should have to OS it
- It should automatically be open sourced on a tight time-scale
- serde_dbus
- Function overloading is bad, actually
- The big kernel refactor:
- Stacks as PL abstraction
- Investigate C++ co-routines
- Cite “state machines” section in Hubris and Humility post:
- Credit John Ericson
- C is not “portable assembly”
- Normal assembly allows you to control the stack
- Operating system implementation of stacks
- Processor implementation of stacks
- Discuss Smalltalk linked list of activation records
- Discuss async
- GC’d Rust converges to Haskell
- See: https://boats.gitlab.io/blog/post/notes-on-a-smaller-rust/
- Memory as a resource is too low-level for most people
- Long live GC!
- Lifetimes is fundamentally tied up with call stacks
- A low level concept
- Mutation, and how Rust tracks it
- Is similar to low level memory management mechanisms
- We can mutate when we don’t have references to old version
- Similarly, we can drop
- Is similar to low level memory management mechanisms
- Haskell has mutation all over the place
- More literally (ST monad)
- Less literally (Reflex’s
Dynamic
)
- serde (research project)
- Generic, monomorphized programming with the policy pattern
- Case study: serde_json
- History of user interfaces and how that interacts with programming
- Fail fast, fail often
- Why error messages should still signal even if the compiler “knows what you mean”
- Scour Tower textbook for blog posts
Tech reading/research:
- https://nostarch.com/introcomporg
- https://nostarch.com/go-hck-yourself
- Research C++ coroutines
- Reach out to John Ericson’s friend, Josh
- Look into reflex-process
Newsletter
Find out via e-mail when I make new posts!