Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutual recursive objects - when to allow them? #600

Open
marzipankaiser opened this issue Sep 23, 2024 · 3 comments
Open

Mutual recursive objects - when to allow them? #600

marzipankaiser opened this issue Sep 23, 2024 · 3 comments
Labels
question Further information is requested requires-design

Comments

@marzipankaiser
Copy link
Contributor

Assume we defined:

interface Ping { def ping(): Unit }
interface Pong { def pong(): Unit }

Then, the following top-level definition is accepted by Effekt (albeit not working for LLVM):

def pinger = new Ping { def ping() = ponger.pong() }
def ponger = new Pong { def pong() = pinger.ping() }

(let's ignore non-termination here, we could add a counter).

Locally in a function, this does not compile (ponger is not defined):

def foo() = {
  def pinger = new Ping { def ping() = ponger.pong() }
  def ponger = new Pong { def pong() = pinger.ping() }
  ()
}

Also, the following (which uses boxing implicitly) doesn't work (locally or globally):

def mkPing {ponger: Pong} = new Ping { def ping() = ponger.pong() }
def mkPong {pinger: Ping} = new Pong { def pong() = pinger.ping() }

def pingr = unbox mkPing { pongr }
def pongr = unbox mkPong { pingr }

IIUC, this means that mutually recursive interface definitions are only possible on the toplevel, and not in LLVM.
Is this the behavior we want? Which of those should work? (Are there interesting other variants?)

@marzipankaiser marzipankaiser added question Further information is requested requires-design labels Sep 23, 2024
@marzipankaiser
Copy link
Contributor Author

Leveraging boxing, we could think it's possible to:

def pingr2 = unbox mkPing { unbox mkPong { pingr2 } }

But this fails with

[error] pingpong.effekt:19:44: Could not resolve term pingr2
def pingr2 = unbox mkPing { unbox mkPong { pingr2 } }
                                           ^^^^^^

@b-studios
Copy link
Collaborator

This is an interesting question, which is also related to #274.

In general, we need to rethink / redesign the "object-system" anyways. Other aspects are:

  • toplevel objects (can those be mutually recursive?)
  • classes
  • implementing multiple interfaces
  • objects as members
  • private methods
  • "extension" methods
  • fields

Maybe the different aspects can be collected somewhere in a "super-ticket"?

@marzipankaiser
Copy link
Contributor Author

Note: Workaround(-ish) I use for local mutually "recursive" objects via unrolling:

def withPing[R]{ b: {Ping} => R }: R = {
  def pi = new Ping {
    def ping() = withPong { {b} => b.pong() }
  }
  b{pi}
}
def withPong[R]{ b: {Pong} => R }: R = {
  def po = new Pong {
    def pong() = withPing { {b} => b.ping() }
  }
  b{po}
}

@marzipankaiser marzipankaiser changed the title Mutual recursive interface definitions - when to allow them? Mutual recursive objects - when to allow them? Sep 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested requires-design
Projects
None yet
Development

No branches or pull requests

2 participants