6 ms·
It absolutely does not provide the same guarantees, and I've covered why in several posts now. Do you believe that people who disagree with you have never used
by Jweb_Guru 5y ago
It absolutely does not provide the same guarantees, and I've covered why in several posts now. Do you believe that people who disagree with you have never used any language other than Rust before? Having to deal with data structure invariants getting violated due to reflection is a huge headache and makes the parsers way less useful than they would be otherwise.
- pjmlp 5y agoYes, because those same people seem to be unaware reflection is not the only way to use such APIs, revealing a superfical knowledge of how those ecosystems have been working for the last 25 years.
- spoiler 5y agoHow is it implemented in Java or C# if not via runtime reflection? I've always assumed that's the way its done...
- andrewaylett 5y agoI'd always recommend folk use constructor-based deserialisation, rather than getting the deserialiser to create empty objects and poke at fields. That's still (at least naively) using reflection, but it's a lot safer. In practice, using reflection for every object being deserialised is far too slow and Jackson at least generates code at runtime rather than repeatedly reflecting. That's magic, but it's nice magic because it's not breaking the language: one can see that it's possible to implement it in pure Java.
- pjmlp 5y agoYou can use stuff like VS T4 templates or compiler plugins via annotation processors. So at compile time, just like serde works, you create your type safe serializer.
- brabel 5y agoJava has annotation processors which can perform code generation, just like Serde does. Manifold [1] an example framework that relies heavily on that, achieving similar things like Serde. Java developers don't like to use these things too much though, unlike Rust developers who love their `#derive`, so you're mostly right that reflection-based serialization is still more common, but that's by choice. Dependency Injection is in a similar situation: frameworks like Micronaut [2] can do it without reflection, and things like Google Dagger [3] have existed for several years that do the same thing... but still, most Spring Boot-based projects I know of use reflection-based DI as well... it's fast and the security issues have been largely mitigated nowadays. [1] http://manifold.systems/ http://manifold.systems/ [2] https://docs.micronaut.io/latest/guide/ https://docs.micronaut.io/latest/guide/ [3] https://rskupnik.github.io/dependency-injection-in-pet-project-dagger2 https://rskupnik.github.io/dependency-injection-in-pet-proje...
- spoiler 5y agoThank you for the detailed write up! I love finding an out about cool language feature/tools
- Const-me 5y agoI don't program Java, but I have implemented and used similar things for C#. Reflection is indeed used there, but not to serialize. It's only used once per type, to generate code. C# has multiple ways to generate code in runtime. One is Reflection.Emit, allows to manually generate bytecode instructions + metadata. For instance, one can build new types in runtime. A typical pattern is implementing manually-written abstract class or interface with generated types: this way the manually-written code can call into runtime generated one. Another method is System.Linq.Expressions. This one allows to generate code (no new types though, just functions) from expression trees, and provides API to build and transform these expression trees. Regardless on the method, the generated code is no different from manually written code. JIT compiler can even inline things across, when generated code calls manually written one, or vice versa.
- spoiler 5y agoAh the bytecode generation via Reflection.Emit seems like a really powerful feature. Does it feel "brittle" to use, or is it expressive enough to aid writing more robust code (to a degree, I realise it's not possible to aliminate every problem)? There's a weird (not in a bad way) elegance here that reminds me of lisp.
- Const-me 5y ago> Does it feel "brittle" to use Yes and no. No because when you try to do unsupported things like calling a method on an object which doesn’t support one, you gonna get an appropriate runtime exception. Yes because if you fail lower-level things like local parameter allocation, you gonna get an appropriate runtime exception but that one is (1) too late, I’d prefer such things to be detected when you emit the code, not when trying to use the generated code (2) Lacks the context. Overall, when I can I’m using that higher-level System.Linq.Expressions for runtime codegen. Things are much nicer at that level. I only using the low-level thing when I need to emit new types, like there: https://github.com/Const-me/ComLightInterop/blob/master/ComLight/Emit/NativeDelegates.cs https://github.com/Const-me/ComLightInterop/blob/master/ComL...
- UK-Al05 5y agoWhy would reflection break invariants? Most deserializers can pass through data into the constructor via reflection as well, where you can protect against invalid objects.