Draft for a proposal for a Pure Function API

It is only for example as I made this proposal compatible with Explicit Ownership Syntax and Explicit Reference Syntax. I just said if any future proposal provide a such functionnality it will be valid as arguments.

You can do this check at parse time as it is a syntax rule of this proposal (or at least at runtime), eg:

const arrow = () => true

fn pure(arrow = arrow /* ...args */) {
  // code
  console.log(/* code */)
  //code
  arrow()
}

Can result in a pseudo AST of shape

{
  type: PureFunction,
  name: "pure",
  args: [/* */],
  body: {
    //...
    type: Function, //Syntax error (Function are not allowed in PureFunction body)
    name: "console.log",
    //...
    type: ArrowFunction, //also throws
    name: "arrow"
    //...
}

Of course these checks can be done at runtime for those that can be deduced at parse time.

When I say can't throw, this is not to have fn that don't throws but you can't use throw new Error() "manually" or using throwable function. It is a sort of like in rust with Result enum that function can return and panic that is raise by the "runtime" to ensure that the function respect the signature.

fn name() {} declaration if a constant declaration unlike with function keyword. As the input and output are "constant" (known before runtime) you could simply unwrape recursive function like:
(only if the function is returning itself)
Only for example, not a guideline of an implementation:

fn recurse(a, b) {
  //...
  a += 0.1
  b = a + b
  if (a < 100) return recurse(b, a + 2 * b)
  if (a < 200) return b
  return a
}

To a tail call optimised form

fn recurse(a, b) {
  while (true) { // all the body of the function is passed in the loop
    //...
    a += 0.1
    b = a + b
    if (a < 100) {
      [a, b] = [b, a +  2 * b] //recursive call is simply transformed into reassignment
      continue // + continue
    }
    if (a < 200) return b
    return a
  }
}

You only have access to globalThis not the global scope in order to have access to standard objects like Date, Map, ... . To use a outer scope object you have to pass it explicitely as argument. For the same reason you can't pass a reference as argument as it can be accessed from the outiside.
supposing the following calls:

const c = new Date()
const d = 5
pure(1, [/* primitives or literals*/], c, d, { key: 'prop', date: new Date() }) //error
//c raise a syntax error since it is an external reference, you have to clone it
pure2(date = new Date(c)) //ok date is cloned

An Explicit Ownership Syntax and Explicit Reference Syntax like syntax can be helpful but with these rules we ensure the "purity" of the function.

I think the idea is pretty robust. There is no need to have type since you can distinguish primitives and Object, const and let. I also forgot to mention it in my previous posts but with the fn keyword you declare implicitely a const pure function and like arrow function there is no access to internal properties like arguments ... I think the best is also to don't bind this.