chapel-lang / chapel

a Productive Parallel Programming Language
https://chapel-lang.org
Other
1.78k stars 418 forks source link

Add a purely functional subsystem #19150

Open skaller opened 2 years ago

skaller commented 2 years ago

Summary of Problem

I propose a function keyword to construct a function in a manner somewhat similar to the existing proc construction.

Functions will have the following properties:

In phase 1 I propose only monomorphic functions. This will be extended to support parametric polymorphism in phase 2. In phase 2, sum types will also be added. Further extensions will be detailed later. Interface constraints should be included but will have to conform to strict requirements to be acceptable.

Requirements.

Comments.

We cannot pass a const entity to a function because the const binding does not guarantee immutability AFAIK. On the other hand an owned object can safely be modified by the function. in particular an owned array can be modified without loss of referential transparency.

Difficulties

A function can use a proc internally. It is hard to verify the result is correct. Initially, this should probably be disabled. Introducing fully type checked and verified functions is, in fact, partly intended to familiarise users with the impact of a well designed type system, to support subsequent repair of the existing proc system.

Options

It may be useful to add pre- and post-condition clauses. Suggested syntax is: requires expr where expr is any boolean and expect expr where expr is any boolean and may also use the keyword result to refer to the final returned value. Like this:

function f(a:int require a>0): int expect result > 0 { ... }

The run time test for the precondition must be generated at the point of call, since any failure is the fault of the caller. The post condition test can be generated inside the function, since any fault is a failure of the function implementation.

skaller commented 2 years ago

Implementation Notes

It may be possible to implement closures with a class with an apply method. The constructor will set appropriate pointers to parent and child closures. A nested function which is only passed to other functions only requires a weak (plain old) pointer to the parent, since the parent lifetime exceeds that of the child (with one nasty special case, a tail call). The function deletes the child closure on exit. In this scenario the parent owns the child, but lends it to other functions.

A nested function closure which is returned requires an owning pointer to the parent and the acceptor of the child closure becomes the owner of the child, and transitively its parent. The new owner must delete the child, which in turn deletes the parent.

There are two options for the pointer to the returned closure. If it is an owning pointer, then the owner cannot call a function and pass it twice. If it is a reference counting pointer, there are no restrictions on use (other than the requirement the closure not be put into a heap data structure).