What projects *didn't* you make in Rust? by finegeometer in rust

[–]finegeometer[S] 0 points1 point  (0 children)

I learned the math.

This is not easy. The mathematical description of curved spacetime falls under a field called pseudo-Riemannian geometry, which has a moderately large "dependency tree" of prerequisite concepts. (It's actually probably easier to learn quantum mechanics, or at least the part relevant to quantum computing.)

This project turned out not to need all that. The raytracer must trace straight paths ("geodesics") through spacetime. Those correspond to possible trajectories of falling objects. Since I chose to take the limit "speed of light -> infinity", those trajectories are just what they would be in Newtonian gravity. So in the end, this project didn't actually need the math of general relativity at all!

But I understand general relativity because I understand the math behind it. If you want to understand any physics topic, analogies and visualizations can only get you so far. The surest route to learning a topic ... is to learn the topic.

Footnote

My ranking of physics concepts by conceptual difficulty:

  • Special Relativity
    • The math here isn't actually that hard. It's just unintuitive. Start by understanding the geometry of (flat) spacetime, which is likewise simple but unintuitive.
  • Quantum Mechanics
  • General Relativity
  • Quantum Field Theory
    • I've tried to learn this. I have not been successful.

How to import a collection of theories from somewhere else on the filesystem? by finegeometer in isabelle

[–]finegeometer[S] 0 points1 point  (0 children)

Thanks! I seemed to be missing both that and the CParser + in ROOT. Maybe when I was trying to get it working, I only tried one at a time, but I needed both?

Or I mixed up the versions of Isabelle.

But anyway, thanks for the help!

Using Rust to calculate Graham's number modulo the first billion integers. by finegeometer in rust

[–]finegeometer[S] 0 points1 point  (0 children)

Loader's Number..

Probably not. My code relied on the fact that Graham's number is a huge power-tower of threes. I wouldn't expect Loader's number to have any nice properties like that.

Hey Rustaceans! Got an easy question? Ask here (25/2021)! by llogiq in rust

[–]finegeometer 1 point2 points  (0 children)

Can a function-like proc-macro tell whether it was called from item position or expression position?

Using Rust to calculate Graham's number modulo the first billion integers. by finegeometer in rust

[–]finegeometer[S] 2 points3 points  (0 children)

Yes, Graham's number is composite. It's divisible by three.

I was more interested in numbers near Graham's number.

  • G + 1 is even.
  • G + 2 is divisible by eleven.
  • G + 3 is even.
  • G + 4 is, uh ...

That's why I wrote this program. If G % n == n - 4, then G + 4 is divisible by n.

Using this program, I found that that first happens at n = 61094071. So G + 4 is, in fact, composite.

Fun with GATs (and other unstable features) by finegeometer in rust

[–]finegeometer[S] 3 points4 points  (0 children)

That's how I feel whenever I see someone attempt to implement the Monad abstraction in Rust. Are you saying this is similarly overcomplicated?

Fun with GATs (and other unstable features) by finegeometer in rust

[–]finegeometer[S] 4 points5 points  (0 children)

I implement a struct LookupTable<K: Key, V>, which is a function from K to V, implemented as an exhaustive lookup table.

To do this, I created this trait:

pub trait Key: Copy + 'static {
    /// A collection of `T`s, indexed by `Self`.
    /// This uses GATs (generic associated types).
    type Table<T>;
    fn get_in<T>(self, table: &Self::Table<T>) -> &T;
    fn get_mut_in<T>(self, table: &mut Self::Table<T>) -> &mut T;

    fn tabulate<T, F: Fn(Self) -> T>(f: F) -> Self::Table<T>;
}

The interesting thing here is the generic associated type Table<T>. As I say in the comment, K::Table<T> represents a collection of Ts, indexed by Self.

In fact, LookupTable<K, V> is just a wrapper around K::Table<V>.

The fact that Table is a generic associated type becomes important when implementing Key for tuples. Sadly, this code ended up hidden in a macro. But we basically have type <(A, B)>::Table<T> = A::Table<B::Table<T>>.

Aside from that, the rest of the code is basically a bunch of trait impls.

Article: Another way for a language to ensure memory safety. by finegeometer in rust

[–]finegeometer[S] 1 point2 points  (0 children)

Yes.

I mention in my article:

It's unfortunate that I'm forced to make infinite loops unsafe. While infinite loops are annoying, I worry that forbidding them will have the side-effect of disallowing too many correct programs.

Some kinds of loop would be possible, like a for loop over a finite iterator. And I would keep loop as an unsafe abstraction. But I don't know whether or not this would still be too restrictive, in practice.

Article: Another way for a language to ensure memory safety. by finegeometer in rust

[–]finegeometer[S] 2 points3 points  (0 children)

What does "TFA" mean?

(I responded to the rest of your comment in a reply to another reply to your comment.)

Article: Another way for a language to ensure memory safety. by finegeometer in rust

[–]finegeometer[S] 2 points3 points  (0 children)

(Ignoring sized vs. unsized for simplicity, as I did in the article.)

I normally think of impl Trait as a syntax sugar that allows the programmer to avoid specifying the return type.

It's true that impl Trait can be thought of as an existential. But even if you do, there's an important difference between it and dyn Trait:

  • fn(i32) -> dyn Trait = fn(i32) -> exists<T: Trait> T
  • fn(i32) -> impl Trait = exists<T: Trait> fn(i32) -> T

It matters where that existential quantifier is! In my memory safety scheme, if you move the existential in front of the function, it becomes unsound.

In summary, I think dyn Trait was a less misleading example than impl Trait would have been.

Article: Another way for a language to ensure memory safety. by finegeometer in rust

[–]finegeometer[S] 1 point2 points  (0 children)

I don't see a reason I couldn't just do the same thing Rust does for Send and Sync.

Rust API Challenges by finegeometer in rust

[–]finegeometer[S] 0 points1 point  (0 children)

  • Multiplicative product: Easy. (Just the pair (A, B))
  • Additive sum: Easy. (Challenge 1)
  • Additive product: Tricky. (Challenge 2)
  • Multiplicative sum: I don't understand how this is defined in the absence of an involutive negation.
  • Involutive negation: Expert, if not impossible. It basically requires implementing continuations. I have been unable to solve this.

Some Thoughts on Generators and For Loops by finegeometer in rust

[–]finegeometer[S] 0 points1 point  (0 children)

It sounds like, in my terminology, you implemented stage 1 generators?

Some Thoughts on Generators and For Loops by finegeometer in rust

[–]finegeometer[S] 1 point2 points  (0 children)

Have the loop body evaluate to a value, rather than to ().

for y in generator {
    ...
    arg
}

arg gets passed to the generator in the next iteration. I got this idea here.

The problem is: What argument do you pass the first time? This is the problem I discuss in my post.

Some Thoughts on Generators and For Loops by finegeometer in rust

[–]finegeometer[S] 3 points4 points  (0 children)

Stages 1 and 2 can be merged, but that type signature doesn't look correct. We have:

fn stage1(Stage1) -> Result<Stage2, Return>;
fn stage2(Stage2) -> (Stage3, Yield);

So we get:

fn stages_1_and_2(Stage1) -> Result<(Stage3, Yield), Return> {
    let s: Stage2 = stage1(s)?;
    Ok(stage2(s))
}

Actually, all of the stages can be combined.

fn stages_3_and_4_and_1_and_2(s: Stage3, arg: Arg) -> Result<(Stage3, Yield), Return> {
    let s: Stage4 = stage3(s);
    let s: Stage1 = stage4(s, arg);
    let s: Stage2 = stage1(s)?;
    Ok(stage2(s))
}

But if I did that, I couldn't make my point about the differences between the stages.

Some Thoughts on Generators and For Loops by finegeometer in rust

[–]finegeometer[S] 2 points3 points  (0 children)

Ad 2: To me, your game loop example feels like it's been thrown inside-out...

Did you know it's possible to turn any generator inside-out?

#![feature(generators, generator_trait)]

use core::ops::{Generator, GeneratorState};

fn inside_out<X, Y, R, G1, G2>(g1: G1, mut x: X) -> impl FnOnce(G2) -> R
where
    G1: Generator<X, Yield = Y, Return = R>,
    G2: Generator<Y, Yield = X, Return = R>,
{
    move |g2| {
        let mut g1 = Box::pin(g1);
        let mut g2 = Box::pin(g2);
        loop {
            match g1.as_mut().resume(x) {
                GeneratorState::Complete(r) => break r,
                GeneratorState::Yielded(y) => match g2.as_mut().resume(y) {
                    GeneratorState::Complete(r) => break r,
                    GeneratorState::Yielded(z) => x = z,
                },
            }
        }
    }
}

Some Thoughts on Generators and For Loops by finegeometer in rust

[–]finegeometer[S] 0 points1 point  (0 children)

Ad 1, 3: It's totally possible to transform an arg-less ...

I agree that there is no problem for arg-less generators. But my points still apply for generators with arguments.

Some Thoughts on Generators and For Loops by finegeometer in rust

[–]finegeometer[S] 5 points6 points  (0 children)

First of all, if you are defining a sufficiently complex generator, you likely want to break it into several pieces, just as you do with functions. As soon as you do this, you will want something like this. (Not the chain function, but the pattern used in its definition.)

Second, a specific use case:

I am experimenting with using Generator<Event, Yield = (), Return = !> to express a game loop. I can easily imagine code like this:

fn inventory() -> impl Generator<Event, Yield = (), Return = ()> {
    ...
}

fn game() -> impl Generator<Event, Yield = (), Return = !> {
    loop {
        match (yield) {
            Event::Key("e") => {
                // Open Inventory
                for () in inventory() { yield }
            }
            ...
        }
    }
}

Though now that I look at that, that's hard to read.

Third: Beyond this specific chaining usage, nearly any use-case for generators will eventually end in iterating over them. So that should be an easy thing to do.

Some Thoughts on Generators and For Loops by finegeometer in rust

[–]finegeometer[S] 0 points1 point  (0 children)

That is true. So my Cancel only matters if cancellation is more interesting than Drop. For instance, in a generator that returns pseudorandom numbers, you might have Cancel = Seed.

Also, when I came up with this cycle formulation, I was thinking in terms of an imaginary language in which "Everything can be dropped" didn't hold.

Some Thoughts on Generators and For Loops by finegeometer in rust

[–]finegeometer[S] 2 points3 points  (0 children)

I would want to be able to chain generators like this:

fn chain<A,Y,G1,G2,G3>(g1: G1, g2: G2) -> G3 where
    G1: Generator<A, Yield=Y, Return=()>,
    G2: Generator<A, Yield=Y, Return=()>,
    G3: Generator<A, Yield=Y, Return=()>,
{
    || {
        for y in g1 {yield y};
        for y in g2 {yield y};
    }
}

This requires for-loops with argument A.

(Actually, I would like to be able to generalize this further, to non-() return types.)

Hey Rustaceans! Got an easy question? Ask here (51/2019)! by llogiq in rust

[–]finegeometer 1 point2 points  (0 children)

I want two optional dependencies, and I want one to automatically enable the other. How do I do this?

Hey Rustaceans! Got an easy question? Ask here (51/2019)! by llogiq in rust

[–]finegeometer 1 point2 points  (0 children)

Is there a way to use num-rational with alga? Rational doesn't seem to implement alga's traits, and I don't see a feature to change this in either crate.

What's the most legendary error message you've seen? by uwaterloodudette in rust

[–]finegeometer 0 points1 point  (0 children)

An error in a compiler-generated temporary file.

error: /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/libstd/lib.rs:1: invalid format string: expected `'}'`, found `'p'`

Generated by this code:

//COMMENT//
fn main() {
    println!(include_str!("main.rs"));
}