Jump to content

JackDunaway

Members
  • Posts

    364
  • Joined

  • Last visited

  • Days Won

    40

Everything posted by JackDunaway

  1. 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)
  2. 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
  3. 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
  4. 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)
  5. 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.
  6. 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
  7. 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.)
  8. 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!
  9. 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]
  10. 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)
  11. Wow... those places are ridiculously cool. What's crazy is that some actually exist, like the Martian Embassy.
  12. 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.
  13. 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.
  14. 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?
  15. 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.
  16. 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.
  17. Quite true -- missed the fact this post is under the "http://lavag.org/forum/44-certification-and-training/" Certification Subforum
  18. I'm impressed with how much quality traffic the Certification Forum (http://forums.ni.com/t5/Certification/bd-p/Certification) gets -- this is the location with the single highest information density about all things CL(?:A|D|AD). You'll find that both helpful experts and the NI Certification team keep close tabs on that board -- check it out!
  19. Short answer: this is expected behavior. Longer answer, keep reading: My response: Thanks for the response! Except... when the goal is to remove static linkages, so that the caller (friended class) can exist before lazy-loading and dynamically calling the callee (friending class), thus loading its extensive hierarchy. The workarounds provided above require static linkages and prevent lazy-loading. Any thoughts on another way to achieve this goal of lazy-loading through invocation of a community-scoped member? And the response: So there you have it! This proxy library pattern is working fine for me as a solution, but I think for the majority of these cases it's easier just to use Public rather than Community scope on the dynamic callee. :-)
  20. It appears that in LV2012, it is not possible to dynamically launch a Community Scoped Method from a class that has been friended by the launchee. I think the screenshot below paints the picture (it just doesn't show that LaunchMeDynamically has friended Launcher)... also attached is the example project. As expected, we can launch a Public Method no prob; we get Error 1390 when attempting to launch a Private Method; but Error 1390 is unexpected when trying to launch the Community Method. Is this a bug with my application or with LabVIEW? DynamicLaunchScope.zip
  21. To adapt the $10k engineering invoice joke -- "$1 for the hammer strike, $99 for knowing where to strike it, and $9900 for all the recreational programming and nights reading LAVA, Darin.K, et al."
  22. Here's a new fix that is much better than the Variant fix: it looks like the "Always Copy" node once again pulls through as the CAR-buster:
  23. Thanks, James! I've chimed in and cross-posted on that thread. (Bad news? This is kinda great news, for my sanity....) And here's a janky workaround, instead exposing a variant on the ConPane then converting inside the SubVI:
  24. Can someone describe why Error 1 happens? It appears you cannot dynamically register an array of control references if the array is connected on the ConPane -- disconnect from the ConPane, and the error goes away. And it's just for arrays, not scalars. (LV2012 VI attached) DynamicEventRegistrationForRefsOnConPane.vi
×
×
  • Create New...

Important Information

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