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

Enumerator based coroutine yielding #281

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

imerr
Copy link
Contributor

@imerr imerr commented Jun 9, 2020

Hey!
One of the features that's missing from moonsharp that'd make writing lua code a lot more elegant would be support for yielding a coroutine in clr functions
In unity this is usually done by just having an enumerator/generator function (not sure of the 100% correct terminology):

    IEnumerator MyCoroutine(int counter) {
        for (int i = 0: i < counter; i++) {
            yield return i;
        }
    }

You can do something like this already, but you'll have to manually keep track of the iterator:

    for i in my_coroutine(10) do
        coroutine.yield(i)
    end

Conceptually it shouldn't be too hard to just add some "syntactical sugar" to implement something similar to the lua function above behind the scenes..
So I decided to give that a try and went digging
First I looked at the custom converters and figured it'd be as easy as checking for an attribute on the method info and just giving it a different DynValue with the "for loop yield" part, but turns out classes don't even use that and I had to go a lot deeper

    [MoonSharpClrCoroutine]
    IEnumerator YieldExample(int counter) {
        for (int i = 0: i < counter; i++) {
            yield return i;
        }
    }

This is obviously a giant hack at the moment and I'm mainly looking for feedback to get this to a decent state where merging it might be considered
The whole MethodMemberDescriptor part can be implemented better, but I didn't want to dig too deep into the scary parts for a proof of concept
I'm also curious what the best way would be to generate the iterator wrapper part? From what I gathered the yield does need to be run in bytecode currently

@imerr
Copy link
Contributor Author

imerr commented Jun 9, 2020

Oh, usecase

The usecase for this would be nicer yielding in api functions while we wait for things to happen/be ready
A dialogue system is probably an easy example

dialogue:say("Hello please give me your name")
local name = dialogue:input()
dialogue:clear()
dialogue:say("Hi ".. name)

Currently dialogue:input() would be implemented with lua wrapper function calling coroutine.yield until we have an input response, but obviously this pretty much means you have to implement every api twice - once in c# for the actual api and once as a lua wrapper for yielding

@imerr imerr changed the title Enumerator based coroutines Enumerator based coroutine yielding Jun 9, 2020
Comment on lines +13 to +23
var enumerateYielder = script.DoString(@"return function (callable)
return function (...)
for y in callable(...) do
if coroutine.is_return_value(y) then
return coroutine.get_return_value(y)
else
coroutine.yield(y)
end
end
end
end", null, MethodInfo + "_yielder");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to parse this lua code one for performance reasons? Could this lua function be pulled out and kept as a global function to be used when needed? I've done something similar in my own code, though all on the lua side without the C# interconnection work this PR does to make it nice and clean.

@imerr
Copy link
Contributor Author

imerr commented Sep 3, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants