Jump to content

JackDunaway

Members
  • Posts

    364
  • Joined

  • Last visited

  • Days Won

    39

Everything posted by JackDunaway

  1. Yes, functional; definitely not imperative/procedural, which is roughly on the same spectrum (yet in a less-desirable direction) as OO. But functional is orthogonal to this spectrum, and promotes at least one tenet that typically competes with imperative and object-oriented programming: minimizing mutable state, and ideally statelessness. An OO approach to the NotifyUser.lvclass example might suggest setting property SetMessage.vi and then invoking DisplayMessage.vi, in the meantime storing Message in the class private data. Functional Programming promotes statelessness, so we would strive to implement Display Message with a ConPane with the complete set of parameters necessary to perform the action. Not coincidentally, this style marries beautifully with event-driven programming. For this reason, I want child class functionality to implement and "override" (I use the term "override" loosely) with different ConPanes than the parent. And I'm clearly stating that this request does not jive with OO purism, so I completely agree with (yet reject as a argument against "Must Implement: ) all sentiments in this thread that "Must Implement" does not adhere to the interface as defined by the parent. Fair enough; but we can predict some. Likewise, I rarely use these either. (yet, mainly because of the limitations described in this thread!! e.g., wanting unique ConPanes for child Implementations). Your second sentence above is perhaps a better verbalization of my sentiments; a subclass who Implements a parent, yet does not Implement a certain method that would be best marked as "Must Implement" (yet not Dynamic Dispatch) is effectively breaking the functionality of the class. The reason the class designer would flag "Must Implement" goes beyond vacuous expectations of what ought a child class to do; it's an indication that "something is broken with this inheritance unless this obligation is being fulfilled". We would probably agree that if the class designer uses this contract much beyond ensuring things don't "break", that the contract is probably being overused and/or misused. This crusade is for a desire to minimize mutable state. Reduce asynchronization between an object's cached state with the subject's physical state. Reduce cognitive overhead of understanding systems (i.e., reading code). Increase opportunities for parallelizability. These are my perceived benefits of functional programming over imperative programming, and why a healthy dose thrown is thrown in with the benefits of LVOOP. It won't necessarily relieve the issues described in this thread; but generally agreed that it will relieve other issues, and is a much welcome improvement. 100% agreed, and you're not on a limb. Consider my calling it a 'compromise' a friendly euphemism for 'mortal sin' This is a new, interesting discussion altogether, but not the solution. It breaks down simply when you want different numbers of parameters on the ConPane for child implementations. (Whereas, genericity describes unknown type for any one parameter) That being said, genericity might alleviate (even, significantly) some of the root problems here.
  2. No -- again, I see *no* negative side effects, no yellow warnings, no linker/compiler errors... let's take this offline, and I can report back to this thread if we learn anything of interest.
  3. So, there's definitely something wrong here. My sanity says 'thanks' for this info. Attached is a snippet that shows just the first few hundred 'issues' -- feel free to run it yourself on your own project; the values from my run are set as default data on the indicator. And again, 'issues' notwithstanding, nothing in the project is broken.
  4. Reviving a bit of an old thread... So when I click on the root project item and invoke "Find Items Incorrectly Claimed by a Library", it returns a list of hundreds of VIs, nearly all of which are in vi.lib. But a few dozen were in my codebase, and so I fixed them; fair enough. (The trivial fix is to manually drag offending members out of the library and then back in; a bit tedious for a few dozen items, but it could be automated through VI Server) Just trying to learn more about this, so... What is the benefit of resolving this 'condition'? What are negative consequences of not resolving this non-reciprocation? I have yet to run across any concrete problems caused by this condition, other than an icky feeling. (Case in point: vi.lib ships with many library members who don't acknowledge their ownership, apparently without problems)
  5. Great tip! Hearty +1! In the past, I've brokered resize events from the parent into the children using the application's interprocess messaging -- using Pane resize events will simplify this :-)
  6. I'm not necessarily looking for a resolution of a problem; rather just comparing notes to what seemed like a similar scoping construct -- mine was one of those ideas that has never graduated past a 30sec stream-of-consciousness dump onto the LV IdEx Google Task list :-) It would be interesting to hear more about the specific context in which you originally envisaged this feature.
  7. Here's a direct copy-and-paste from a note I wrote a couple years ago on the "potential IdEx Candidates" list: The gist is to truly scope data to the BD of a single VI, discouraging abuse of exposing data across a SubVI (or even structure) boundary where it does not belong. Is this idea kinda like what you're proposing? Or an alternate solution to solve the same problem? (One obvious difference: this declaration would be held at the VI level, not the object level)
  8. Can you give a concrete example? For this new feature, do you envision restricting fields to exactly 1 class method, or a defined subset of class methods?
  9. Not really a joke -- some branches of design (Metro, notably) actually are headed toward flat and rectangular, like the current rasterization of LabVIEW -- didn't mean to sound flippant. Maybe you'll find ways to accomplish your goals using the Icon API directly from <vi.lib>LabVIEW Icon API; or perhaps with the LV2012 Icon Editor source; hope this is helpful.
  10. Two years later -- I'm reporting back that .lvlib is absolutely better than .lvclass without the concept of an "object" or "statefulness". An important performance consideration -- previously unmentioned in this thread -- is that when a member of an .lvclass is statically linked to a caller, the entire .lvclass and its members and their static dependencies are loaded into memory. On the other hand, an .lvlib grants random access to its members without loading the entire library and dependencies. Typically, I'm not one to promote performance micro-optimization, but this consideration is one that can easily take seconds and affect end-user experience of your application. (And a few tags for the googles: Dependency management, static linkage, lazy-loading)
  11. The stock Icon Editor actually uses and stores icons as 24-bit color -- yet the icon preview on FP/BD, and also instances on a BD -- are rendered with a palette that very nearly resembles (or, is exactly) the 216-color web-safe palette, plus 10 each for a spectrum of R, G, B and greyscale to make 256 colors. In other news, 8-bit and minimalism (e.g., "Metro") is one branch of latest design trends -- we could turn the conversation positive and just call it ahead of its time
  12. Me too!! But in the spirit of not hijacking this thread, perhaps let's continue discussion on the XTab thread to help both OP and Olivier
  13. Nice! Your concept tracks refreshingly smoothly for a native LV solution! By the way, another great example of this concept is XTab by SAPHIR. Olivier created an impressive video to demonstrate (the embedded video is currently broken on that site; here's a direct link to the video: http://www.dailymotion.com/swf/xksiht -- the part you're interested in starts around 0min:38sec)
  14. Agreed, a Spolsky fan also, and keeping software engineering on the lithosphere. Yep; this shows the power of 'Must Call Parent', which also would exist for 'Must Implement' as it does today for 'Must Override'. This type of code re-use contract is not unique to Dynamic Dispatch, but equally important with Must Implement. Then I sit here scratching my head how the object is determined run-time, yet one concrete implementation requires a different ConPane than another. Again, I'll stand by the statement that if any methods in a Dynamic Dispatch hierarchy need separate ConPanes, Dynamic Dispatch was the wrong choice, and your comment on the Block Diagram "Not Used; Only to make ConPanes identical" seems like a red flag... it's the desire for the three current language benefits of overrides (defining the parent-child relationship, ability to require implementation, and ability to require calling parent for object construction and re-use), just without the requirement of the defined ConPane.
  15. Aha! This is a perfect example where Dynamic Dispatch 'appears' to be the correct solution, yet it's not. Any override with unused or incoherent inputs on its interface is better replaced by simple static methods in the concrete classes. The parent should not define the superset interface of potential child implementations, where concrete class callers "just should be aware" of which inputs they should be wiring (that makes for a weak API) Your example is a canonical example for 'Must Implement' -- the Static Concrete Implemented Method is a more appropriate construct than 'Must Override' with Dynamic Dispatch. Yet you, and me, and others make the compromise (or make the mistake) of sometimes using Dynamic Dispatch purely for the Must Override and Must Call Parent contract. And I'm *totally* not ragging on your code, Shaun -- rather, happy to see another respected developer show code and admit what we've created "felt wrong". I bet we're not the only ones, and I'm determined to hash out a resolution for this, whether a new ability to 'Must Implement', a design pattern to account for this, or just better personal coding practices. Finally, a few common instances where Dynamic Dispatch is often the incorrect choice where Must Implement 'feels right': the initialize() function of concrete object types that requires unique sets of parameters to construct the object; a Serializer, Logger, Messenger, or DisplayNotifier class who expects its children to serialize(), yet understands and allows for their need for strongly-typed unique inputs; basically, *any* time you ever find yourself considering a variant or stringly-typed input or output to accomodate for unique interface requirements
  16. I believe the 'abstract' concept from other languages typically defines the full function prototype, including parameters. Which makes 'abstract' more akin to Dynamic Dispatch than Must Implement. I'm suggesting Must Implement does not define the function parameters for subclasses. (But perhaps Must Implement does require a minimum set of parameters; the object input, for instance, would be required in order to invoke 'Call Parent Method' <headscratch>gotta chew on that one</headscratch>). (And a sincere virtual+1 to you for joining this thread without using the term LVPOOP Perhaps the term 'functional programming' piqued your interest Just ribbing.)
  17. Aha. I understand. Had to read that many times, but I see we're probably coming from two different angles. The spirit of what I'm suggesting emphasizes how concrete classes interface with calling code. It's decidedly functional-programming-esque, not OO-purist by any stretch of the imagination. You're defending the case for OO-purism, where unknown concrete interfaces (ConPanes) of 'Must Implement' methods would be clearly incompatible with inversion of control, where superclasses must know the interface, by definition. The usage of 'Must Implement' best jives with calling code that instantiate concrete, static classes. These concrete instances fundamentally represent functional programming, and incidentally enjoy the OO benefits of encapsulation/access scope/inheritance of abilities/and contractual requirements of subclassing (woot!). On the other hand, an OO-purist application might inject dependencies at runtime, where fundamentally the application is abstracted and transcended into a higher state of being. Both approaches are valid! Yet if inversion of control is not an application domain requirement, I choose concrete implementations. Why? Static analysis by the compiler is your friend. +1 for compile-time errors vs. runtime errors 1446 or 1448. That's all -- admittedly, the suggestion of 'Must Implement' is not OOP-y, and I'm still curious if any other languages address (either, have implemented, or have explicitly rejected) a notion such as 'Must Implement'. And to quote you, 'please understand I'm not trying to be hostile' -- just laying it out there, and learning to be a better programmer like the rest of us!
  18. That's a reference to the LVOOP implementation of the Command Pattern in LabVIEW, and its portly codebase. As a codebase grows, two other things grow greater-than-linearly: cognitive overhead required to understand a program, and the liability of a codebase owner. (And length of tea time, waiting for your build. But we like tea time.) "Programs must be written for people to read, and only incidentally for machines to execute." --Abelson & Sussman, SICP, preface to the first edition Another quote (that even Google cannot produce right now) says something about object-oriented and functional programming being roughly orthogonal, and the sweet spot of productivity for your language du jour comes by acknowledging the syntax and constructs of your language. It's just an opinion, but Command Pattern and subclassing every message is a beautiful and pure concept, but too close to the OO extrema to be practical. Syntactically and logistically burdensome. [Crouching, waiting for tomatoes to be thrown]
  19. Sounds like you need to fire that second-rate subclass designer Just joking of course -- the goal of this language feature is to help convey information to developers about the intent of subclass design -- to help you implement the correct things. Not make things hard. If you're attempting to subclass in a manner that far outside the original class designer's intent, it's perhaps time to consider composition rather than inheritance. That's your code smell. Nope! Yeah, agreed. (And, AQ, what are your thoughts on Must Implement?) On the other hand -- on the subject of 'same' names -- I see no reason one could not mark multiple methods from one subclass as Implementing a Must Implement parent method -- with the requirement they have unique filenames and therefore function names. Why is this desirable? To have access to the Call Parent Method; and to establish relationships and intent through native language features. (This ties back to the same wish for overrides for Dynamic Dispatch, where a GUID property of the class is the basis for contract fulfillment of a 'Must' flag, rather than filenaming convention. Even writing this, I cringe thinking "Oh yeah, and development of this other GUID linkage feature gates the development of Implemented multiplicity..." Nevertheless...) To illustrate with the Display Message example, DisplayMessage.vi could be Implemented by DisplayMessage-Bool-YesNo.vi and DisplayMessage-Bool-YeaNay.vi You see that and think, "Aha! Canonical use case for subclassing NotifyUser-Bool.lvclass with NotifyUser-Bool-YeaNay.lvclass and NotifyUser-Bool-YesNo.lvclass and then overriding DisplayMessage.vi with Dynamic Dispatch! Gotcha, suckaaaah!" But what if we wanted a different flag on the ConPane to for YeaNay to print "Yay" for some dialects and "Yea" for others? We're back to a case where Dynamic Dispatch is just the incorrect tool. Finally, these 'Must Implement' and related 'Require Implemented to invoke the Call Parent Method' features do not grant new abilities that cannot already exist by diligent convention. But I prefer compiler feedback to diligence or conventions (EDIT: removing double post)
  20. Wow... those places are ridiculously cool. What's crazy is that some actually exist, like the Martian Embassy.
  21. No worries! I would never think that. The burden's on me to provide a good enough example to show the value (tl;dr -- skip to the example below the horizontal rule) A superclass is unable to call an Implemented method, just as a superclass is unable to call a static dispatch methods of its descendants. You know, the error "You have connected a refnum of one type to a refnum of another type and both types are members of some class hierarchy, but there is neither a simple up cast nor type cast between the two classes." So, I think your question indicates you're still seeing Implemented methods like a Dynamic Dispatch override. There's no sense of run-time polymorphism. It's just a static method in the subclass whose existence is necessitated by its parent. The parent says, "If you're going to have an IS A relationship to me, you gotta define this method. Implement it however you want, I don't need access to it and I'm never going to invoke it, I just gotta ensure you're taking care of business." In the original post, I debated on whether to call this new option 'Must Implement' or 'Must Overload' or 'Must Supercede'. Implement seemed like the most correct term, since it's not overloading, per se (the method is in a separate namespace, and is only available to the Implementing class and its descendants -- an overloaded method seems like the overloading functions would have the same namespace and access scope, so overload seems like the wrong word). On the contrary -- AF has disrupted the way I look at systems and interprocess messaging. Holistically, it's a hammer that shouldn't be swung at every nail, but the principles it teaches supercede the framework implementation itself. (To Jon: and we haven't talked in a while, I've contacted you to see if I can get a demo of your UI implementation with AF!) Maybe it's a weak argument, but if one sees the value in "Must Override", that same magnitude of value exists with "Must Implement". It's just the parent's ability to ensure a functionality exists in descendants; one happens to have run-time polymorphism and a defined interface, the other doesn't. Here's a simple example. Consider the class, NotifyUser.lvclass which only has one method, DisplayMessage.vi, whose purpose is to display a string to a user. Later down the road, you consider it worthwhile to display other types of things to users, like Bools and Numerics, so you decide to subclass NotifyUser.lvclass since it's already got all the business logic for displaying messages. The illustration below shows this: Notice how the two implementations have separate ConPanes from the parent. A need for separate ConPanes is always an indication that Dynamic Dispatch is the incorrect design choice, and I used to try to solve this (incorrectly) by weakly typing the inputs as variants or strings -- a bad API design choice. Also tried using classes, but then the same problem exists, just with a new abstraction layer added! (This is one of the syntax/logistical burdens of the Actor Framework implementation, and more generally the LVOOP approach to the Command Pattern in LabVIEW) That's where Must Implement saves us -- the ability to establish a relationship between child and parent methods, without requiring them to have the same interface. Another thing to note above is that the two children are broken with the message "This VI attempts to override a static VI in an ancestor class." This is due to today's design limitation that subclasses cannot have the same method names as parent methods, except in the case of overrides. Further, note that in the middle diagram, use of the "Call Parent Method" is unrecognized. These two issues would be addressed by a "Must Implement" feature, shown below (note: this is not valid syntax with LabVIEW today, but through image manipulation we're able to demonstrate the proposed syntax) Now, with "Must Implement", we have the ability to contractually require functionality in subclasses while maintaining the ability to extend parent class functionality, having acknowledged that Dynamic Dispatch is the incorrect tool when distinct function interfaces are desirable. One final note to further stress the power of this new contractual relationship: "Must Implement" methods could additionally require "Implementations must call Parent Implementation", à la Dynamic Dispatch.
  22. This becomes syntactically burdensome and impractical. Dynamically-dispatched methods in this Command Pattern cannot readily accept/return unique parameters that are scoped locally to the caller (e.g., one method accepts a Boolean wire from the caller, while another accepts a Numeric wire). Additionally, this statefulness of the caller does not necessarily belong in the object just to circumvent this issue (now you have a LVOOP-style-clustersaurus). Case study: try creating two user interfaces requiring request-response communication with Actor Framework (e.g., any sufficiently advanced SubPanel architecture), then examine the sequence diagram (timing diagram) of communication. You cannot wire what is not created :-) Creating an "Implemented" method is the exact same workflow as creating a Static Dispatch method; it's just that the LVClass is broken before its creation, and happy once created. Unlike Must Override, which defines a both the name and parameters of a function prototype, a Must Implement would only require a name and leave parameters (ConPane I/O) up to the child class. The parent class is saying "if you want to be like me, you have to do at least this; you can do it however you want, but you gotta do it." Finally, the superclass could itself Implement and still mark itself as Must Implement -- just like Must Override. The subclass creating the Implemented method has access to the "Call Parent Method" LVOOP ability, which would effectively drop a static method of the parent implementation, while still reserving the freedom of defining its own new interface.
  23. When subclassing, methods can be defined as Dynamic Dispatch, allowing implementations to override the parent. These implementations must have the same ConPane interface as the parent. Further, the parent can (awesomely) declare "Must Override" per Dynamic Dispatch method, a contractual requirement of the subclass to implement the method. We're cool so far -- this is just a definition of how things work. Here's where things get tricky: it's *so nice* to have that "Must Override" flag, yet you wish that implementations had a different ConPane than the parent method. Clearly, Dynamic Dispatch would be the incorrect choice, yet it hurts to give up "Must Override". So what do you think: could there exist a concept of "Must Implement"? It's like "Must Override", yet does not require the subclasses implementation to adhere to a ConPane interface. These Implemented methods act like a static method of the the class, only callable by the class and its descendants (this is not like Dynamic Dispatch, since a broken wire would result from wiring a Parent object to an Implemented method). The parent simply requires implementation -- the existence of a method -- without defining the interface. Do other languages have this concept? Are there design patterns that address this need? Or is this desire for "Must Implement" indicative of poor OO design choices?
  24. And here's the guerrilla guide: Take a good stab like a jolly good sport at closing all your references Every blue moon when you run into a bug caused by improperly closed references, run Desktop Execution Trace Toolkit and take 5minutes to fix the yellow rows. Prosper. Now the *tricky* part comes with, not closing your references -- but keeping references open -- by understanding lifetime w.r.t. dynamic processes. I'm still refining my understanding and best practices to deal with this limitation.
  25. Ran across a new programming language today - vigil from https://github.com/munificent/vigil ---- and you thought *LabVIEW* sometimes leaned too-restrictive in favor of being beginner-friendly. From the vigil readme: I am now seriously considering using the phrase "The ever vigilant watchers of your code have found malfeasance in: %s" for of my custom error call chains in LabVIEW.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.