5 ms·
Consider the Enums simply as defunctionalized closures. https://blog.sigplan.org/2019/12/30/defunctionalization-everybody-does-it-nobody-talks-about-it/ https:
by jerf 5y ago
Consider the Enums simply as defunctionalized closures.
https://blog.sigplan.org/2019/12/30/defunctionalization-everybody-does-it-nobody-talks-about-it/ https://blog.sigplan.org/2019/12/30/defunctionalization-ever...
There are significant advantages to this sort of defunctionalization in a system like this. It is a bit more administratively at the point of enum definition, but the flexibility and simplification of the other parts of the program will be a win for almost any non-trivial UI application, not to mention the future-proofing it gives you.
- amelius 5y agoCould you give a concrete example of the benefits? I prefer to avoid abstract discussion since it might not apply to 99% of my code. And by the way, defunctionalization sounds a lot like transforming my code into a bytecode interpreter which interprets my code. This is something I don't need and which would make my code slower and more difficult to read.
- b3morales 5y agoOne key difference is that the defunctionalized representation is serializable. An example feature that enables is that you can now have a GUI for your interface construction, letting you preview the layout while you build it. The controls are hooked up to the message names, which can be be deserialized at runtime and used for dispatch. Attaching live functions, especially closures, to them is not really feasible.
- amelius 5y agoDo you think that at one point Rust could automatically transform my code into defunctionalized form, and thus make it serializable? What you describe sounds like QT Creator, by the way.
- b3morales 5y ago> Do you think that at one point Rust could automatically transform my code into defunctionalized form, and thus make it serializable? For a pure function some kind of bytecode representation is certainly conceivable, but as soon as you add captures it gets much more complex. > QT Creator Precisely, yup; or Android's Layout Editor/Apple's Interface Builder.
- verdagon 5y agoYou might be serializing your enums, but unless you serialize a lot more (button IDs, view IDs, etc), the IDs in your enums will be meaningless. I don't think you're suggesting we serialize the entire GUI? This also seems like the wrong layer to serialize. Normally we would want to serialize the model, not the view. Admittedly, the line is a bit blurry here (and for most small programs) so take my words with a grain of salt.
- b3morales 5y ago> I don't think you're suggesting we serialize the entire GUI? I am, exactly -- to enable WYSWIG construction/editing of the GUI. Just added another comment saying this below, but consider Android Layout Editor/Apple Interface Builder (or someone else mentioned QT Creator, which I'm not familiar with but has the same idea).
- amelius 5y agoYou might be right, but I feel that by filling out Enum structures I am helping the tools, whereas the tools should be helping me. And as a terminal-aficionado I don't want to use Design/Creator tools. So ... what I really want is a simple API with closures. And there should be no problem with that, as the Design/Creator tools can simply emit more complicated code if they want (e.g. closures that use enums).
- verdagon 5y agoI'm not sure this is how WYSWYG really works. There's only one MyFancyTextEditor widget definition, a blueprint so to speak. It's IDs refer to other things inside that blueprint. But at runtime, we might instantiate two different MyFancyTextEditors, one for view A and one for view B. They probably shouldn't share IDs, lest we run into confusion. I think WYSWIG is more like defining a class, whereas at runtime we'd be serializing instances. Feel free to correct me if I'm wrong, I admit I've never been down this line of thought!
- b3morales 5y agoHere's an example snippet from a xib file for an actual iOS project of mine: <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ykl-6F-b5r"> <rect key="frame" x="0.0" y="0.0" width="375" height="0.0"/> <state key="normal" title="Done"/> <connections> <action selector="doneEditingTitle" destination="POI-12-VR5" eventType="touchUpInside" id="14R-fR-990"/> </connections> </button> A xib is an XML description of a GUI layout. Note the `<connections><action ...>` element inside the `<button>` -- this is defining what happens when the button is tapped. The connection becomes active at runtime when the file is rehydrated into live objects. The `selector` string identifies the name of the method to be called. (This exact mechanism relies on ObjC, but the principle can be applied to other systems.)
- jerf 5y agoThe problem with closures is that they are opaque. The only way to interact with them is to call them. Making them the foundation of your system eliminates a lot of useful architectural patterns, such as putting filters on the event stream, being able to prioritize some of them based on certain criteria, being able to ship them between different servers if necessary, creating a generic logger that can operate on the enumeration values without having to have each closure log things, implementing various security checks (you really don't want a security system to be stuck only being able to run a bit of code to see if it's safe), just a whole lot of things that can't be done if all you have are closures. If you don't care about your values leaving your local OS process, which is a pretty common use case, there's a hybrid approach you can take too, which is to put closures inside your data structures that describe the value, instead of passing raw closures around. You get the benefits of being able to examine the values without executing the handler and being able to filter, decorate, etc. but while retaining all the advantages of being able to implement handlers inline. Depending on your local language, various slick implementations may allow this to be one degree or another of transparent, such as implementing some sort of interface/trait for all these values, or implementing __call__ in Python, etc. "And by the way, defunctionalization sounds a lot like transforming my code into a bytecode interpreter which interprets my code. This is something I don't need and which would make my code slower and more difficult to read." It depends on the language you're in. Most OO languages have some way of doing this that doesn't add any speed issues to speak of, it's just a slight rearrangement of code. I'd also say this is an issue of scale. I don't bother with this in small programs and may just glue everything together with closures, but as program size increases the odds that you'll want to do something that you can't do with closures directly increase. But the languages I tend to work in don't make this much of a hassle usually, either. I think you may be overestimating the expense.
- verdagon 5y agoBy opaque, do you mean encapsulated? Encapsulation is pretty important to software architecture, in that it enforces decoupling between components. Rust has private visibility for this very reason. The filtering you're talking about sounds interesting, but the GUI layer is (imo) definitely the wrong place to do it. When's the last time you wanted to filter, prioritize, secure, or distribute UI events? Imagine if we transformed every method call in our app into enums, it would be an unwieldy and unreadable mess. I'm not saying you're suggesting that, just highlighting that there are definitely some drawbacks here. So, I don't see why we're being forced to use enums in this case. I wonder if this a conscious decision by the Iced folks, and if they first tried with closures and ran into some trouble. Your hybrid idea is interesting! Is that doable with Rust in practice? I'd imagine the borrow checker might enforce it can't read from outside its scope.