effekt-lang / effekt

A language with lexical effect handlers and lightweight effect polymorphism
https://effekt-lang.org
MIT License
319 stars 23 forks source link

Captures in function signature #307

Open dvdvgt opened 11 months ago

dvdvgt commented 11 months ago

Currently, we differentiate between effects and capabilities. Capabilities in a function's signature are to be seen requirements to the call-site and not the actual effects a function may use. This is different to most other languages featuring algebraic effects, where one can easily identify pure functions just by looking at a functions type. For example

def f(): Unit / {} = ...

does not have any requirements for the caller, that is, there are no effects that need to be handled. However, f may still look like this

def f(): Unit / {} =
    try {
         ...
         l.log(...)
         ...
    } with l: Logging {
         ...
    }

even though one may think it is pure at first glance. For the FFI, we currently have the following valid syntax:

extern effect io
extern {io} alert(): Unit = "alert('hey')"

where we can specify which effects a function uses, not requires. Similarly, I propose also make this syntax available to normal definitions:

{ Logging } def g(): Unit / { Logging } = ...
     |                           |
     captures                effects

Here, g uses the effect Logging (left-hand side curly braces) and requires handling of the Logging effect (right-hand curly braces). For f, one would need to write { Logging } def f(): Unit / {} since it uses the effect Logging but does not require handling of that effect. Using this notation, the user can ensure that a function uses only the specified effects and none other. This also allows the user to directly identify/specifying pure function (un-effectful functions) by writing {} def g .... It should also be possible to just omit it and then everything functions as of right now.

Since the effects that are being used are already tracked, this should not require drastic changes and was already (partly) implemented for an OOPSLA-2022 paper.

I can imagine this to be really useful, since it could be great for explaining Effekt's way of dealing with effects and lifting some confusion about the notation of capabilities in conjunction with pure functions. Since it should be completely optional, it won't get in the way of the user if they should not choose to use it.

dvdvgt commented 11 months ago

As it is perhaps the most common use-case, we could introduce the syntactic sugar pure:

pure def f(...): T    :=    {} def f(...): T / {}