VOID WRONG

It seems like I misunderstood some stuff.


Over the years, up until Rust, there were 4 ways to have a ‘safe’ language:

  • A virtual machine (e.g. most languages today) — whether it’s a high-level (scripting languages) one or low-level one (JVM, CLR)
  • What C++ and similar languages do, destructors, compile-time bound checks, make a global heap and shit in it, make a local stack and piss in it, etc (basically my AllocPPx.pl but baked into the language)
  • Bake the VM in with the machine code. That’s what D and Go do, and I think Nim does that too. This will make the point of your language being compiled entirely moot imo.
  • Finally, the most ‘controversial’ decision for imperative-loving chuds: make a functional language. ML/Scheme/CLisp achieve memory safety through closures. Haskell achieves this through Monads. Functional languages have a property which allows them to be be both compiled into machine code and bytecode, and also, interpreted like an scripting language.

The problem with all these approaches is the trade-off between safety and speed. There’s another factor, and that is low-level access to the system. Languages like OCaml came close to achieving a balance, and that’s why Rust bassed itself on it.

Most imperative languages have ‘operational semantics’, but not ‘denotational semantics’. You can’t describe what C does with math. What C does depends on the platform, the CPU, etc.

Rust’s safety is achieved by ‘flattening out’ the memory model of a functional language like Ocaml. OCaml is a language with denotational semantics, because it’s a functional language. Rust is an imperative language but it has denotational semantics. At least when comes to memory management.

I am not going to even attempt to describe the denotational semantics of Rust because I am just an amatuer and i don’t have a master’s in LDT. But if someoen tries, they could.

I think people might have already done it. I am not sure.

If you tell me no, and Rust does not have denotational semantics, I stand by my great-great grandfather’s barber’s grave that yes, it does!

So why do I say Rust ‘flattens out’ the functional model for memory management? It does at least with lifetimes. So imagine this: lifetimes are just ‘let’ bindings, but with a different syntax.

OCaml:

let rec factorial = function
  | 0 -> 1
  | n -> n * factorial (n - 1);;

Scheme

; This uses `let` under the hood
(define (factorial n)
  (if (<= n 1)
      1
      (* n (factorial (- n 1)))))  

So these two in Rust would be:

fn factorial<'a>(n: u32) -> u32 {
    match n {
        0 => 1,
        _ => n * factorial(n - 1),
    }
}

I know 'a is almost useless here, but what I meant was, that 'a makes it similar to the ‘let’ bindings in the prior to examples!

Semantics here is clear. Right?

But C:

int factorial(int n)  {
   if (n == 0) return 1;
   else return n * factorial(n - 1);
}

We do have operational semantics here, but who’s to say what are the denotational semantics? Right? What is a ‘function’ in C? Well most C compilers translate it to an Assembly subroutine, but what if our target does not support labels, or subroutines?

You see what I am arriving at?

Conclusion

Rust is a semi-functional, semi-imperative language, but the difference between Rust and other languages with functional aspects is: denotional semantics!

Note: A language having lambda closures does not make it ‘functional’, you can do that in GNU C too ffs! Denotational semantics make it functional.

  • FizzyOrange@programming.dev
    link
    fedilink
    arrow-up
    11
    ·
    7 months ago

    Quite a lot of misunderstanding here I think.

    This will make the point of your language being compiled entirely moot imo.

    Not at all. Compiling to machine code rather than bytecode isn’t about avoiding having a runtime. Hell, C has a runtime. It’s called the C RunTime (CRT).

    ML/Scheme/CLisp achieve memory safety through closures. Haskell achieves this through Monads.

    No they use garbage collection and bounds checking just like imperative languages. There are some slight differences but those are nitpicks.

    Functional languages have a property which allows them to be be both compiled into machine code and bytecode, and also, interpreted like an scripting language.

    Absolutely nothing to do with being functional or not. Dart is not functional and it supports AoT and JIT compilation.

    You can’t describe what C does with math.

    You absolutely can describe what C does with maths. I understand it’s very hard but check out CBMC for example.

    What C does depends on the platform, the CPU, etc.

    So does Rust to some extent, e.g. usize.

    Semantics here is clear. Right?

    I have no idea what you’re trying to say here. The lifetime is completely irrelevant here because you have no references.

    What is a ‘function’ in C? Well most C compilers translate it to an Assembly subroutine, but what if our target does not support labels, or subroutines?

    The C specification says what a function is.

    You see what I am arriving at?

    Not even remotely lol

    • First off, I apologize if I did not add a disclaimer saying I could be wrong. But given this, what is exactly the difference between denotaional and operational semantics? I base what I said on [my understanding of books about language theory. But it seems like I got the wrong gist. Where do you recommend I start?

      PS: I’ll add a void to the post rn.

      • FizzyOrange@programming.dev
        link
        fedilink
        arrow-up
        1
        ·
        7 months ago

        Honestly I have no idea. I started reading the Wikipedia page on denotational semantics and it doesn’t explain it very well but as far as I can see, denotational and operational semantics are just two different ways of describing the same thing. It almost seems to be just a philosophical difference, like frequentist Vs Bayesian.