-
Notifications
You must be signed in to change notification settings - Fork 8
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
Passing lambdas into child models allow them to execute arbitrary parent model code #133
Comments
Had missed this issue, so closing #135 and copying in my thoughts here: As we all learned when @LaCuneta wrote his response here, https://stackoverflow.com/questions/54332081/netlogo-levelspace-how-to-pass-strings-between-two-child-models, callbacks are currently possible in LevelSpace. My use case is LevelSpace child models acting as input to each other. I've made a child model that allows kids to use their mouse to draw a graph. This graph is then returned to the parent model who can then use it as model input (@cbradyatinquire you might like this, so tagging you :) ). I can make it work without a callback, but from a user experience perspective, a callback makes it so much nicer. I know we've talked about this many times, and I know there are potential pitfalls. For modeling purposes, they also break with some Netlogo language convetions. But they are SOOOOOO nice to have for these more advanced use cases with LevelSpace. Can we keep them, @qiemem ? What, if anything, would we need to do to make sure that they don't break a model? |
I understand the problems relating to parallelism. I wonder if, as we've talked about a few times before, that we should have a way to enable and disable it, probably with it enabled by default. That way, we can let people do whatever they want. (Or more like, then I can keep doing what I want :) ) I honestly really love the ability to code NetLogo models as an interface or visualization of other models. It's just really cool, and useful.
To me, this is the best argument against it. I was using a callback today because I was using a child model as an interface (as seen above) to data in another model, and it was not easy understand what was going on and in which model what was running. Especially because I had some gui threading issues that forced me to distribute code across the child and the parent. When/if we can do LevelSpace model declarations inside the Code Tab that might change, but right now it is quite difficult to write this code.
They are in |
Agreed! Generally I prefer to do this the other way though; the visualization model is the parent model. It's usually easier to reason about. It also keeps the actual model agnostic to how it's visualized, which I also think is good. I confess, however, that callbacks still have use here when you need to track event-based stuff (e.g. births). Beyond parallelism, there a few semantic issues to think about here:
won't compile because the parent model doesn't know what
compiles, but breaks at runtime. If we allowed this, we would have to only allow numbers, strings, nobody, and lists thereof to be passed in as arguments to the lambdas, just as was supposed to be the case between models. We would also need to ensure that the semantics are, in fact, otherwise consistent, and that they are guaranteed to be so. That means that the only thing that can refer to something in the child model in a lambda are the arguments. |
Yes, and it also doesn't quiet work in the case where the other model is not just a visualization but an interface can can be used to manipulate parts of the model. In my case, I want data shown in the child model, but those data can be changed, and then sent back to the parent. The only way to do that without a call back is to have a while loop in the parent that constantly checks the child model to see whether it is in a state where the user is "done" with it. Doing that locks the rest of the parent model which is really frustrating for the user because they can't do anything else with the other model in the meanwhile - and more importantly often leaves the user thinking that the model is hanging, causing them to restart NetLogo and losing their work.
I'm completely fine with doing that, but is it even possible? LevelSpace doesn't really know anything about what is passed into the lambdas during runtime, does it? Would this not require changes... to the core?!?
I'm totally for this, but again, can we do anything to actually ensure this other than encourage users to follow this pattern? |
See https://stackoverflow.com/a/54369364/145080.
That's a pretty awesome use, but it will also break bad when running multiple children simultaneously. Child models, by default, run in parallel, which means there's no way to control what order they will run code in (even with
random-seed
). Normally, this is fine, since child models can't directly affect each other. But passing procedures in mean order matters, so the results of the above code will be unpredictable, even if you userandom-seed
. Worse, child models don't synchronize on the same object (otherwise parallelism would be pointless), and global variables are not effectively marked volatile, if two child models runin the parent, they may both read the un-updated
foo
before the other hasn't finished its update, resulting infoo
being incremented once, not twice.The other reason why this isn't supposed to work is more philosophical. All inter-level logic is supposed to live in the parent. Allowing any child to talk directly to any other child results in code that is very difficult to reason about and debug.
The safest option here is to just block anonymous procedures from being passed around (as I thought they were). Another option, which addresses the technical concern, but not the philosophical, is to disable parallelism if anonymous procedures are being passed around.
The text was updated successfully, but these errors were encountered: