bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
34.36k stars 3.36k forks source link

Use type-safe functional-style lenses instead of the Reflect trait #10862

Closed pillowtrucker closed 4 months ago

pillowtrucker commented 7 months ago

What problem does this solve or what need does it fill?

The very first thing that is asked of the user is to implement the Reflect trait. It looks like something lenses would be much better at. Neither is runtime reflection required for proper serialization. Moreover, with lenses, instead of "path strings", one could use simple function composition to access nested data.

What solution would you like?

Use lenses instead of runtime reflection.

What alternative(s) have you considered?

Start out my rust project by removing static type checking.

Additional context

From a quick look around, it seems that someone has already implemented this with macros https://github.com/plausiblelabs/lens-rs, which is probably the way to go with this, since even with haskell's rich type system you still need TemplateHaskell to do this properly. Here is a c++ solution: http://vitiy.info/functional-lenses-for-cpp14/, and the canonical haskell implementation: https://hackage.haskell.org/package/lens-5.2.3

irate-devil commented 7 months ago

The very first thing that is asked of the user is to implement the Reflect trait.

I'm curious, what gave you this impression? Most of the time reflection isn't super relevant in Bevy outside of serialization. Though you did mention it.

nicopap commented 7 months ago

I come from a functional background, and at first I was doubtful about the approach bevy has to reflection. And I had the same preconception as you "why not use serde?" "why not use lenses?" After dealing with reflection in Java, and its many obscure runtime errors, and then discovering how much cleaner and less error-prone it is in Haskell, I thought reflection was just an inferior way of doing things. But I can tell you, in game dev there is a usecase for reflection.

Notably, lens-rs doesn't solve what Reflect solves. Some examples:

So, I don't think we should "replace" reflection with lenses. Lenses are useful and definitively solve a class of problems, but those are different problems than bevy_reflect solves, even if they overlap sometimes.

As a final thought, we can implement a system that is as performant as lenses, as type safe, with less complexity, using bevy's reflection system. We just associate a path with a PhantomType. See https://github.com/bevyengine/bevy/discussions/10285

pillowtrucker commented 7 months ago

The very first thing that is asked of the user is to implement the Reflect trait.

I'm curious, what gave you this impression? Most of the time reflection isn't super relevant in Bevy outside of serialization. Though you did mention it.

I was looking specifically at https://github.com/bevyengine/bevy/blob/latest/examples/scene/scene.rs#L19 to figure out if I can store a scene/level layout in a format that I could read from different engines/editors if I don't want to re-arrange it from scratch in every editor separately. I have a level in UE that I wanted to recreate and I really don't want to do it a third time if I could avoid it.

pillowtrucker commented 7 months ago

I come from a functional background, and at first I was doubtful about the approach bevy has to reflection. And I had the same preconception as you "why not use serde?" "why not use lenses?" After dealing with reflection in Java, and its many obscure runtime errors, and then discovering how much cleaner and less error-prone it is in Haskell, I thought reflection was just an inferior way of doing things. But I can tell you, in game dev there is a usecase for reflection.

Notably, lens-rs doesn't solve what Reflect solves. Some examples:

* Listing fields, describing structure of the type, for UI display such as in https://github.com/jakobhellermann/bevy-inspector-egui.

* [Trait reflection](https://docs.rs/bevy_reflect/0.12.1/bevy_reflect/#reflecting-traits). Useful, again to have specialized UIs per type

So, I don't think we should "replace" reflection with lenses. Lenses are useful and definitively solve a class of problems, but those are different problems than bevy_reflect solves, even if they overlap sometimes.

As a final thought, we can implement a system that is as performant as lenses, as type safe, with less complexity, using bevy's reflection system. We just associate a path with a PhantomType. See #10285

I see there's quite a bit more context to this than I've been able to find before. I'll have a good read through those and try to process them.

james7132 commented 4 months ago

Given the use cases that cannot be enumerated at compile time with lenses, like "animate anything" for game animation, that are heavily reliant on reflection, this issue likely is unactionable without a rearchitecting all of those features in the engine. Closing this out as a wontfix.