Type parameters
#1
Posted 23 October 2009 - 01:33 PM
I posted a new article to ExpressionFlow regarding type parameters. For me it is the one of the most missed features in LabVIEW. Check out the post.
Introduction to Type Parameters
EDIT: Seems like the links to expressionlfow still don't work from LAVA. Please copy paste from below
expressionflow.com/2009/10/23/introduction-to-type-parameters/
Tomi
#2
Posted 24 October 2009 - 02:18 PM
I don't get itHi
I posted a new article to ExpressionFlow regarding type parameters. For me it is the one of the most missed features in LabVIEW. Check out the post.
Introduction to Type Parameters
EDIT: Seems like the links to expressionlfow still don't work from LAVA. Please copy paste from below
expressionflow.com/2009/10/23/introduction-to-type-parameters/
Tomi
Labview IS a strictly typed language (only functions of the same type can be connected and a conscious decision to "cast" must be made to other types) as opposed to (say) PHP which isn't (anything can be assigned to virtually anything). Delphi is another example of a strictly typed language whereas C is loosely typed (I think).
The add function you describe is a strictly typed primitive with polymorphism which, in other strictly typed text based langauges, would be an overloaded function.
So I don't really see what your getting at. What am I missing?
Founder and general mischief maker on www.labview-tools.com.
SQlite aficionado and websocket zealot.
If it 'aint in LabVIEW, then you 'aint got a clue!
#3
Posted 24 October 2009 - 09:35 PM
Maybe you are misunderstanding the concepts of static (strict) and dynamic type systems. Static type system is a type system that doesn't allow runtime type errors to occur whereas for dynamic type system runtime type mismatches can occur. Static type system can still be very flexible, indeed almost as flexible as dynamic type system. Type parameters allow creating statically typed functions and classes etc. with polymorphism without actually needing to duplicate code. You can for example write a QueueClass class that behaves exactly the same as queue primitive functions in LabVIEW including similar kind of polymorphism for both queue element type and queue reference type. Unlike with dynamic languages, you could never connect something with incorrect type to QueueClass methods, in exactly the same way as you cannot do the thing with LabVIEW primitives. So what you could get with proposed type parameter bases type system is LabVIEW primitive like flexible polymorphic behavior for custom developed types.I don't get it
Labview IS a strictly typed language (only functions of the same type can be connected and a conscious decision to "cast" must be made to other types) as opposed to (say) PHP which isn't (anything can be assigned to virtually anything). Delphi is another example of a strictly typed language whereas C is loosely typed (I think).
The add function you describe is a strictly typed primitive with polymorphism which, in other strictly typed text based langauges, would be an overloaded function.
So I don't really see what your getting at. What am I missing?
Type parameters is one way to do generic programming. Quoting wikipedia: "Generic programming is a style of computer programming in which algorithms are written in terms of to-be-specified-later types that are then instantiated when needed for specific types provided as parameters." Type parameters is about specifying to-be-specified-later types defining relations between these generic to-be-specified-later types as well as setting limits to generic to-be-specified-later types. So you can do a lot of
#4
Posted 24 October 2009 - 11:19 PM
So you can do a lot of...
Hmm... perhaps he was dictating?
----------------------------------------
I agree there are times when Labview generics would be useful. How would you implement them in a way that maintains Labview's "everything is data" paradigm?
Certified LabVIEW Architect
Dak's First Law of Problem Solving: If the solution looks simple, I don't know enough about the problem.
Yes, the QSM is flexible. So is Jello. That doesn't make it good construction material.
There are two secrets to success:
Secret #1 - Never tell everything you know.
#5
Posted 25 October 2009 - 08:58 AM
I agree there are times when Labview generics would be useful. How would you implement them in a way that maintains Labview's "everything is data" paradigm?
I guess you are referring to the fact that VIs using generic types need to be runnable. That is indeed a good question. What is needed is a default value for generic types used as type parameters. For unrestricted type parameters this should be rather easy; there is no need for any particular value. So unrestricted type parameters would behave the same as LabVIEW Object class. The default value would equal to the top most class in the class hierarchy. The default value of a generic type with a single upper bound can be represented with the default value of the upper bound type itself. The default value of a generic type with a single lower bound can be represented with a default value of the parent class of all other classes, which currently is LabVIEW Object class.
The more challenging is the case when multiple upper bounds have been used for a single generic type. The usage of multiple upper bounds from different branches of class hierarchy is possible only if either mixin classes are supported or interfaces are supported (I prefer mixin classes over interfaces), or there is some other kind of support for multiple inheritance. Let's consider the option of mixin classes which would in a LabVIEW implementation have a default value. Let S be any type that inherits from any two classes U1 and U2. For this to be possible, S needs to be a class in type space defined by direct sum of U1 and U2. The same applies for any generic class T that has upper bounds U1 and U2. So the default value of the generic type parameter T that has upper bounds U1 and U2 would be a direct sum of default value of U1 and U2. In less mathematical terms, the default value of T would be a data structure similar to a two element cluster with default value of class U1 and default value of class U2. For more that two upper bounds, this would generalize as a direct sum of upper bounds {Ui}.
So far we have assumed upper bounds are non-overlapping. That is for any two upper bounds U1 and U2, there is no class Q in the inheritance hierarchy for which Q is a common ancestor for both U1 and U2. The easiest solution would simply not allow this kind of type bounds and also disallow multiple inheritance from common ancestors trough two different inheritance branches, unless of course this ancestor class is LabVIEW Object or similar built-in class. There are other options as well, but they are not relevant for the discussion now.
#6
Posted 25 October 2009 - 12:40 PM
I think the difficulty is that the type philosphy of a language is generally defined at the languages conception as to whether the reponsibilty of type information should be in the compiler or the run-time environment (or both and how much in each). Those that choose "compiler" or "run-time" are pretty much stuck with it and only the hybrids are capable of moving towards natively supporting one or the other. Thats not say though that strict typed systems are unable to "emulate" dynamic typed systems or vice versa. Its just its not natively supported which is I think is what you are suggesting for LV.
I think also its a personal choice as to what a particular programmer programmer is comfortable with. The arguements are stacked equally on both sides. Dynamic typing has the advantages you have outlined. But it is also a lot easier to shoot yourself in the foot. Even with the polymorphic vis that Labview provides, it is considered good programming style not to use this feature and instead explicity cast it to the receivers expected type for this reason. For that (amongst others), I prefer strictly typed languages and (if there is a choice of equivelents) choose the strictly typed every time since I don't have many feet and theres too many holes already
Founder and general mischief maker on www.labview-tools.com.
SQlite aficionado and websocket zealot.
If it 'aint in LabVIEW, then you 'aint got a clue!
#7
Posted 25 October 2009 - 01:49 PM
Err.. I think you misunderstood. Sorry, I need to try to be more clear. I am talking about only static i.e. compile time type systems. Forget dynamic languages for now, I am not referring to dynamic type systems. I don't think dynamic type systems fit to LabVIEW dataflow paradigm very well.I think I see what you are saying.
In a statically typed language like LabVIEW, type checks are done at compile time. In LabVIEW the type checks are done actually at development time, but it doesn't matter for now. The whole idea of static type checking is to prohibit type mismatch at runtime. This far we both agree.
Now generic programming IS NOT the same thing as dynamic type system. Generic programming can be made 100% type safe using type parameters and compile time type checking with a static type system. All type related bugs can be avoided like currently in LabVIEW. The actual type comes into play when you place a VI that uses generic types on a block diagram. When you connect a wire with a specific type to a input that depends on some type parameter T, the type parameter T gets bound to the type you connected. That fixes the type of all inputs and outputs that depend on this particular type parameter. In dynamic programming, the type of the output terminals would be of type Dynamic. With type parameters the type of the output is fixed to something specific instead.
As an example, consider a method creating a LIFO (last in, first out) stack of anything. In dynamic programming you would have an input of type Dynamic. The output terminal returning the stack would be of type Dynamic as well. In type parameter based generic programming the input would be of generic type T and the output would be of type Stack[T] (stack of items of type T). When you connect say integer I32 to the input of the create stack method, then the output would become fixed to a type Stack[I32] (stack of items of type I32).
#8
Posted 25 October 2009 - 02:54 PM
Indeed. But most of your article was about specifying dynamic types.Err.. I think you misunderstood. Sorry, I need to try to be more clear. I am talking about only static i.e. compile time type systems. Forget dynamic languages for now, I am not referring to dynamic type systems. I don't think dynamic type systems fit to LabVIEW dataflow paradigm very well.
Well. Except for terminology (I think). I was talking about "Strict" type checking rather than "Static" linking. LV doesn't type check at compile time. It does it before compilation to make sure you don't do anything the compiler will object to. Its more like "syntax" checking.In a statically typed language like LabVIEW, type checks are done at compile time. In LabVIEW the type checks are done actually at development time, but it doesn't matter for now. The whole idea of static type checking is to prohibit type mismatch at runtime. This far we both agree.
OK. I was under the impression that you were promoting dynamic type checking rather than "Generic Programming" (which I've never heard of before) since most of the article was about types. Re- reading it for the 8th time and in that context, your comments in this thread make a bit more sense (hopefullyNow generic programming IS NOT the same thing as dynamic type system. Generic programming can be made 100% type safe using type parameters and compile time type checking with a static type system. All type related bugs can be avoided like currently in LabVIEW.
We can already wire a number of types to (taking your example) an add function. Is the only concern that it fixes the output to the same type? Are you saying that we should also be able to wire the inputs of an add function to (say) 2 double numbers and wire directly to a string without converting first?The actual type comes into play when you place a VI that uses generic types on a block diagram. When you connect a wire with a specific type to a input that depends on some type parameter T, the type parameter T gets bound to the type you connected. That fixes the type of all inputs and outputs that depend on this particular type parameter. In dynamic programming, the type of the output terminals would be of type Dynamic. With type parameters the type of the output is fixed to something specific instead.
Hmmm. Obviously not what I said previously then.As an example, consider a method creating a LIFO (last in, first out) stack of anything. In dynamic programming you would have an input of type Dynamic. The output terminal returning the stack would be of type Dynamic as well. In type parameter based generic programming the input would be of generic type T and the output would be of type Stack[T] (stack of items of type T). When you connect say integer I32 to the input of the create stack method, then the output would become fixed to a type Stack[I32] (stack of items of type I32).
This is what is really confusing
Founder and general mischief maker on www.labview-tools.com.
SQlite aficionado and websocket zealot.
If it 'aint in LabVIEW, then you 'aint got a clue!
#9
Posted 25 October 2009 - 03:49 PM
That's part of it, but more importantly how do you create a generic type in Labview? What does it look like?I guess you are referring to the fact that VIs using generic types need to be runnable.
class Array[T] {
typedef T;
typedef U >= T;
param head : T;
param tail : Array[T];
create_array('array in' : Array[T], elem : T) -> ('array out' : Array[T]) {
'array in'.head = elem;
'array out' = 'array in';
}
add_element('array in' : Array[T], elem : U) -> ('array out' : Array[U]) {
'array in'.tail = 'array in';
'array in'.head = elem;
'array out'='array in';
}I don't have much experience with generics and the syntax is a little confusing, but here are a couple obstacles I see:
- In your example code you have the variables T and U representing arbitrary data. Labview doesn't have variables, only data. To implement the above code in Labview you have to create some sort of data construct representing T and U that you can drop on a front panel. How do you create a data type that, by definition, has no type?
- The first two lines in the class define the relationship between T and U based on their type information. How do you establish that relationship in Labview? It can't be in a vi; the relationship has to be enforced at compile-time, not at run-time.
As I understand it, generics emulate some characteristics of dynamic typed systems by automatically generating the correctly typed code at compile time. Generics in a statically typed language still produce statically typed code. Violating the generic's defined typing rules results in a compiler error, which translates into a broken wire in Labview.Thats not say though that strict typed systems are unable to "emulate" dynamic typed systems or vice versa... Dynamic typing has the advantages you have outlined. But it is also a lot easier to shoot yourself in the foot.
It is!?Even with the polymorphic vis that Labview provides, it is considered good programming style not to use this feature and instead explicity cast it to the receivers expected type for this reason.
Certified LabVIEW Architect
Dak's First Law of Problem Solving: If the solution looks simple, I don't know enough about the problem.
Yes, the QSM is flexible. So is Jello. That doesn't make it good construction material.
There are two secrets to success:
Secret #1 - Never tell everything you know.
#10
Posted 25 October 2009 - 04:17 PM
Actually Labview does type check at compile time. Labview is always compiling your code behind the scenes while you're in the dev environment. Ergo, in Labview compile time == dev time. (Compiling is part of the process of building an executable, but building an executable != compiling.)LV doesn't type check at compile time. It does it before compilation to make sure you don't do anything the compiler will object to. Its more like "syntax" checking.
No. I imagine a properly implemented generic vi in Labview would behave in much the same way as a polymorphic vi, except that you wouldn't have to write individual sub vis for each data type you want to support. You create a single vi with 'type placeholders' and Labview automatically creates the correct "sub vi" based on what you connected to the input terminal.Are you saying that we should also be able to wire the inputs of an add function to (say) 2 double numbers and wire directly to a string without converting first?
Some prims do, but we can't create classes or vis that do this. That's what Tomi is asking for.Don't most of the primitives do this already (numeric functions, comparisons etc)?
Edited by Daklu, 25 October 2009 - 04:18 PM.
Certified LabVIEW Architect
Dak's First Law of Problem Solving: If the solution looks simple, I don't know enough about the problem.
Yes, the QSM is flexible. So is Jello. That doesn't make it good construction material.
There are two secrets to success:
Secret #1 - Never tell everything you know.
#11
Posted 25 October 2009 - 05:00 PM
I'm no expert on compilers (and I avoid knowing about low level stuff like the plague when I can). But i don't think LV continuously compiles. If I remember correctly, it only does it when you click the "run" button. But that may have changed in recent versions.Actually Labview does type check at compile time. Labview is always compiling your code behind the scenes while you're in the dev environment. Ergo, in Labview compile time == dev time. (Compiling is part of the process of building an executable, but building an executable != compiling.)
So all we are really talking about is a "macro" for creating polymorphic vis?No. I imagine a properly implemented generic vi in Labview would behave in much the same way as a polymorphic vi, except that you wouldn't have to write individual sub vis for each data type you want to support. You create a single vi with 'type placeholders' and Labview automatically creates the correct "sub vi" based on what you connected to the input terminal.
We can certainly make polymorphic vis (like the read from spreadsheet file.vi). The only difference between the prims is you cant see the source. There may be a technical difference since we all know LV is really just a big API with a graphical front end, but from the users view it is the same. Its the same with XControls and native controls.Some prims do, but we can't create classes or vis that do this. That's what Tomi is asking for.
Founder and general mischief maker on www.labview-tools.com.
SQlite aficionado and websocket zealot.
If it 'aint in LabVIEW, then you 'aint got a clue!
#12
Posted 25 October 2009 - 05:16 PM
Well. Its not the only reason.It is!?
I've never heard that and strongly disagree with it. (Until someone can convince me otherwise.) It seems plain silly not to use them in places where they are appropriate.
Founder and general mischief maker on www.labview-tools.com.
SQlite aficionado and websocket zealot.
If it 'aint in LabVIEW, then you 'aint got a clue!
#13
Posted 25 October 2009 - 07:45 PM
My apologies. I assumed you were referring the compiling that takes place when building an executable.I'm no expert on compilers (and I avoid knowing about low level stuff like the plague when I can). But i don't think LV continuously compiles. If I remember correctly, it only does it when you click the "run" button. But that may have changed in recent versions.
In this post AQ describes the compiling process in LV and compares it to C++. I read it as something that happens continuously, not just when the run button is pressed. I could be wrong though.
Ehh... not quite. "Super-macro" might be more accurate. Wikipedia has this to say...So all we are really talking about is a "macro" for creating polymorphic vis?
"Both macros and templates [The c++ version of generics - Ed.] are expanded at compile time. Macros are always expanded inline; templates can also be expanded as inline functions when the compiler deems it appropriate. Thus both function-like macros and function templates have no run-time overhead.
However, templates are generally considered an improvement over macros for these purposes. Templates are type-safe. Templates avoid some of the common errors found in code that makes heavy use of function-like macros. Perhaps most importantly, templates were designed to be applicable to much larger problems than macros."
(Tomi has a much better understanding of the differences between c++ templates, type parameters, and other generic implementations. He might have more to say regarding this question.)
Yes, but the difference is with polymorphic vis we have to manually implement the code for any type we want the polymorphic vi to support. With generics we can implement the code for a single "generic method" and Labview will automatically generate the required code for any data type we wire into it.* (Provided the real data type meets the constraints defined by the generic.)We can certainly make polymorphic vis (like the read from spreadsheet file.vi).
Recently I built a Collection class, which was essentially an attempt to implement .Net style collections. It has the standard methods... Add, Remove, Count, Item, etc. However, to really duplicate .Net collections I'd have to create and implement child classes for every possible combination of native and user-generated data type. Not only is that a lot of duplicate code (since, for example, the code behind every child class' Remove method is identical except for the data type,) but there's an infinite number of combinations. Clearly covering all potential inputs is an impossible task. If generics were available in Labview, I could write the class once and use it anywhere. Less code. Less maintenance. Less headache (presumbably.)
[*This leads me to another question for Tomi. As far as I know generics in other languages don't produce new source code--there is a clear distinction between pre-compiled code and post-compiled code. In Labview that distinction is muddied... in a sense all our block diagrams are post-compiled code. Would the code generated by a generic be saved as a separate vi, in which case the feature behaves almost like a toolkit, or do you create it dynamically on an as-needed basis?]
Meh... not a good enough reason for me. If you're trying to optimize a tight real-time loop, then yeah... but my apps run on desktop computers on a preemptive multitasking os. I have bigger things to worry about than an extra data copy here and there.Well. Its not the only reason.
It also creates a copy of the data.
Edited by Daklu, 25 October 2009 - 07:48 PM.
Certified LabVIEW Architect
Dak's First Law of Problem Solving: If the solution looks simple, I don't know enough about the problem.
Yes, the QSM is flexible. So is Jello. That doesn't make it good construction material.
There are two secrets to success:
Secret #1 - Never tell everything you know.
#14
Posted 26 October 2009 - 01:31 PM
Something similar. We are talking about some technique of creating code acting similar to polymorphic VIs, without the need of duplicating the code. But not only that. We are also talking about custom _polymorphic parametrized types_. An example of a polymorphic type in LabVIEW is array. Array can contain any datatype imaginable and array type includes the information of the array element type. Even though LabVIEW has polymorphic parametrized types such as arrays and queues, developers are currently not allowed to develop new such types.So all we are really talking about is a "macro" for creating polymorphic vis?
Unlike with C++ templates, with generics the possible runtime types are not known at compile time. Of course the all possible types are known, but that type space is much too large, so a binary code cannot just be generated to all possible types. Generics are not replaced by the exactly typed counterparts at compile time like C++ templates. Instead a generic binary code is generated, or alternatively binary code is generated just-in-time before at execution time.Tomi has a much better understanding of the differences between c++ templates, type parameters, and other generic implementations. He might have more to say regarding this question.
As I mentioned above, a generic binary code for generic functions is generated. So for generic classes there would be a single generic class. For generic VIs there would be a single generic VI with a single binary code. As LabVIEW VIs have a state (controls, uninitialized sift registers), this is the only way to implement generics in LabVIEW I can think of. In LVOOP method VIs with dynamic dispatch input terminals, the method call is bind to actual method VI at runtime. In a similar manner in generic programming, the method calls to primitives or other subVIs are bind at runtime depending on the runtime type of the generic. As the type checking is happening at compile time, there always exists a method or primitive to be called. Runtime type checks can never lead to a situation where the generic code cannot be executed.[*This leads me to another question for Tomi. As far as I know generics in other languages don't produce new source code--there is a clear distinction between pre-compiled code and post-compiled code. In Labview that distinction is muddied... in a sense all our block diagrams are post-compiled code. Would the code generated by a generic be saved as a separate vi, in which case the feature behaves almost like a toolkit, or do you create it dynamically on an as-needed basis?]
EDIT: I was checking details of C# implementation of generics. When primitive types like Float and Int are used for type parameter, a separate code is generated at runtime. When class types are used, then code is shared betwen different types. That is List[Float] and List[Int] would have their own binary code but List[String] and List[List[Float]] would share the binary code. In all cases, the code is generated at runtime, when first needed.
#16
Posted 27 October 2009 - 04:36 PM
Ada allows generic programming (and has done so for quite some time) and OCaml has a very nice type system.












