Later (D8+) Delphi features source http://dn.codegear.com/article/34324 What's this? - A text document I stored some discussions over later Delphi features in, to be used as src for a wiki article sooner or later. Note that some of the opinions in the upper half are original, authors might have changed opinion due to facts that appeared later in the discussion The numbering: 0. inlining. (not mentioned by the rest because pretty equivalent between FPC and Delphi) 1. operator overloading 2. class helpers #3. strict private #4. strict protected 5. records with method !6. class abstract !7. class sealed 8. class const 9. class type 10. class var 11. class property 12. nested classes !13. final methods 14. sealed methods 15. static class methods !16. for in loop. # implemented in 2.4.0 ! implemented post 2.4.0 <<<<<<<<<<<<<<<<<<< Almindor's comments >>>>>>>>>>>>>>>>>>>>>>>>>>> 1. operator overloading is in fpc longer, and more powerful 2. class helpers aren't valid 3. strict private is illogical 4. detto 5. .NET complement (see C#) 6. illogical 7. illogical 8. useful 9. I wouldn't do it but it might be useful 10. we already have this in C++ mode? 11. possibly useful 12. useless 13. useless 14. useless 15. we already have that? 16. syntax sugar <<<<<<<<<<<<<<<<<<<<<<<<<< Thorsten's: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> what is useful ? I partially agree with 1. But the fact that fpc does not distinguish between implicit and explicit in case of assignment operators is a huge problem, something needs to be done there 2. class helpers... that's rather debatable... I would like something like a "type helper" or some other form of syntax that would allow me to define methods I can call on types like strings. integers and so on... only syntactic sugar, I know.. but still we had discussion of 8, 9 in core some time ago i did notice a lot of C# like ideas all those "private" stuff is because of C# way of class visibility it has no reason in pascal units 3. I disagree, strict private has it's point if you really really want to make sure that you don't accidentially depend on an implementation detail of another class in the same unit 4. ditto Thorsten|NexusDB, that's not logical in unit perspective units were ment to be visible to themselves 5. (Records with Methods) I find very nice. for plain records they are syntactig sugar.. but nice sugar... for *generic records* they are quite important 6. (class abstract) makes sense if you want to prohibit that a specific class gets ever instantiated... a non abstract class with abstract methods can be instantiated, an abstract class can't 7. (class sealed) very useful for optimization purposes (all virtual methods in a sealed class are implicitly final, compiler can generate static calls to final virtual methods instead of having to go through the VMT, big optimization there) 8. (class const) nice to have 9. (class type) has it's uses as you otherwise can't use types in the private part of an class without having that type visible.. but I'm not sure I like the syntax in delphi for it.. the chrome syntax for nested types is a lot nicer IMHO (no idea if possible) "final" should be automagical no it can't the compiler should know which class is last only if you never ever implement a system like packages... and packages is something that's really missing in fpc packages won't happen anytime soon I think (not implying to the final here, just informative) the rtl isn't stable enough but I think that package lose reason in fpc might well be... but I don't think anything should be put into the compiler with directly opposes the later implementation of packages most people use fpc for cross-platformness sure, I agree hmm gtg eh.. that was 10. 11. (class var) no idea if fpc somehow supports that, but it goes with class properties and class methods and makes sense.. feel free to continue I'll read it when I get back 12. (nested classes) is really just the logical consequence of 9. (class type) 13. (final methods) as I said, very very useful for the optimizer... not only can final methods be called as static methods, they can also be inlined which virtual methods normally can't 14. (sealed methods) no idea what Nick was smoking there.. there are final methods and sealed classes (which implies that all virtual methods are final)... there are no sealed methods 15. (static class methods) no idea if that's supported or not in fpc.. I see it of relatively limited value... mainly a .net compatibility thing 16. (for-in loop) you might consider it syntax sugar... but it's rather tasty sugar ;) <<<<<<<<<<<<<<<<<<<<<<<< Marco >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> General remarks: the fact that Borland had to blow up related "features" like class to a separate entry each to get to 16 new features in 3-4 delphi versions says enough I think. The quality is poor, to say the least. Also I think the author of Borland's document did little to make users enthusiast for these features. Very typical is the comment with class type and nested classes. Opague classes would be one of the few (or actually at the moment my only one) information hiding features I could see serious use for. (later edit: this is now a bit moot because D2009 generics are much better for that) Half of Thorsten's remarks strike IMHO a similar excusist tone as the original. Most of these features are filler and dumb copying, not design breakthroughs. I maybe should add that I always (and still) regard information hiding as something first order, and don't really believe in micromanaging every aspect of it. (IOW it should be a guard against first order engtanglements. Once you start micromanaging it, IMHO one takes a bigger risk to introduce errors that one prevents). Maybe this micromanaging is driven by binary component resellers though. (to avoid override tricks). Still IMHO it is not healthy. 1. (reply to Thorsten) Examples? I can imagine it matters, but real examples are more convincing. 2. Class helpers are IMHO extremely dangerous, since a forgotten USED unit can disable the extension afaik. Too dangerous to be useful. Probably Borland had some special purpose in mind, since I can't imagine they'd develop such an ugly beast as general purpose feature. Or the braindrain at Borland is worse than I thought. Components being distributed DCU only might be a reason, since then class helpers are the only way to add some utility methods to existing code. 3..4 strict private and protected are IMHO redundant. I don't buy Thorsten's argument. If it is that critical, and should be that isolated, move it to another unit and alias it to keep compat. I must note that this is mostly a result of me not being so big on the more detail aspects of information hiding in general. Other FPC devels have a different view on this (afaik e.g. Micha and Jonas). 5. IMHO totally useless an sich. But earlier discussions with Thorsten, he made a pretty strong point that this feature is related to for..in, to limit the scope where procedures that implement the iterator functionality (or overloading) need to be searched (to the record itself) Now Thorsten seems to hint on a similar issues with generics, which could be related, but some examples would be nice. It makes those features a lot easier to implement though, since you don't have to make exceptions for suddenly added VMTs everywhere. In retrospect that might be the main reason. 6..7,13,14,15 Pretty useless, except maybe for commercial component builders to steer usage to avoid unforseen use of exposed implementation details. OTOH the idea that component builders can (and do) really future proof map out such details is IMHO slightly naieve. I don't expect implementations that use (abuse?) this to really lower the rate of errors. Too dumb to even deserve the name syntactic sugar. So syntactic makeup. Looks nice from a controlfreakerish gut feeling, but I think in practice it is useless. Thorsten's optimization angle seems far fetched, since it'd be easy for a linker to find out if a class is overriden? His class abstract comment is even more funny. What advantage does this translate to in real life? How many mistakes does this really avoid that aren't catched on the first run or by the compiler that sees references to abstract methods? IMHO Thorsten tries to provide motivation that never existed here. It is plain and simple copying to get feature lists like this filled. Nothing more. 8..11,12 "class type/nested classes" is the only possible useful one here, because maybe it can be used to have opague classes when used in protected. (you don't export the "inner"class, but do export overridable methods that use them. Strangely enough the "nested classes" example doesn't demostracte it, which scares me. The existance of the other (class var/const) seems to be the beginner OOP misconception that global variables are evil, always. Even when already in a separate scope (unit). Of course the average beginner only knows Java or C# as OOP language, and has no clue about modules at all. (summarised by "if it isn't in a class then it is old") (btw: why would a const in a class be useful at all?). Note that FPC has procedural property, which is eq to class property except for the scope. (iow proc property stands to class property as normal global "var" to class var ) (in retrospect, some of the nested type stuff might be because of generics. The reason for this is that the inner type contains generic types, and thus one will be instantiated for each specialization). We know .NET generics were in D2007, so maybe in D2006 times they already had this on the horizon. 13. See earlier remark on sealed* optimization. I don't see why the linker can't figure this out and additional syntax is required. published methods maybe, but the general tone (and thus its yield) is way to broad. (It makes it a global optimization, and that is hard specially in the case of packages) 16. Syntactic sugar, sure. It also seems to indicate set logic (IN and the general syntax), while afaik the implementation is more vector based(at least iirc from a previous discussion with Thorsten?) The latter is IMHO the big problem. Something truely iterator like in nature (so that changes in the datastructure during FOR.IN would be always safe) would be better. Specially since the VCL shows its age and the whole iterator concept is missing. <<<<<<<<<<<<<<<<<<<< Comments on the above that I still have to process in the above part>>>>>>>>>>>>>>>. Of course simply copying some feature is easier, and also fills feature lists. If I misquoted sb, please let me know, since I want to turn this into a wiki article, since this all comes up way too often. Initial comments: :56 < Thorsten__> class properties with virtual class methods as getter/setter have an advantage over plain "procedural properties" because they can be called through a metaclass ("class of ..") variable 19:58 < Thorsten__> as for sealed/final the linker has nothing to do with that? the code to call a method as static call or virtual call is generated by the compiler long before the linking stage.. and the linker can't change code anymore except for updating pre-linking reference markers with final (absolute or relative) addresses? 19:59 < Thorsten__> the linker also can do nothing about inlining... that's also something that much much earlier is done by the compiler 20:01 < oliebol> True. Sorry, was confused with elimination from VMT of unused calls 20:01 -!- Thorsten|NexusDB [n=Thorsten@203.49.205.209] has quit [Read error: 60 (Operation timed out)] 20:02 < oliebol> Still I have some doubts about the warantting syntax. OTOH, there is some analogy to inline, to which the same arguments apply. 20:02 < oliebol> Of course if you'd defer codegeneration you could still do it, but those are still the more experimental compilers. 20:03 < oliebol> About class properties: but what does it matter? You can't override them, so you just collect the set of class properties up to the definition of the metaclass. 20:03 < oliebol> While I agree that is possible, I fail to see something useful there. 20:03 < Thorsten__> sure... great example for that is singularity (about defering code generation, and allowing global optimizations) 20:04 < Thorsten__> what do you mean you can't override them? sure 20:04 < fpk> oliebol: why not in the wiki? 20:04 < Thorsten__> if the getter and setter are virtual class methods 20:04 < oliebol> fpk: this is the draft for the wiki. 20:04 < oliebol> See last line 20:05 < Thorsten__> you can have derived classes which override the virtual class methods... if you then have a metaclass variable and access the property through it the appropiate overriden virtual class method will be called 20:07 < fpk> doing virtual method optimization without sealed is _really_ hard 20:07 < fpk> as soon as any dyn. library is involved it's not possible 20:07 < Thorsten__> and breaks down the moment you allow something like packages 20:07 < Almindor> I don't think it's worth the optimization tho 20:08 < fpk> Almindor: did you ever compare the timings of a correctly and mis/not predicted call on modern CPUs? 20:08 < Thorsten__> I wouldn't say that... the performance advantages in certain cases can be quite significant 20:08 < fpk> real virtual calls trash also the branch target buffer 20:09 < Almindor> true but the use case is what? 5%? 20:09 < fpk> overall? 20:09 < Almindor> how many classes in RTL/FCL can we seal? 20:09 < Almindor> I'd say none 20:09 < fpk> of course not 20:09 < fpk> but the users can 20:10 < Almindor> they won't :) 20:10 < Almindor> well some optimization aware individuals MIGHT 20:10 < Thorsten__> if you mark a class as sealed then all code inside that class itself that calls virtual methods of itself can encode them as static calls.. that can make a huge difference 20:10 < Almindor> but those would rather use objects 20:10 < fpk> Almindor: most optimization is about special cases 20:10 < neli> it would be useful if TList or TStringList etc. could be sealed 20:10 < Thorsten__> and there are numerous places in NexusDB where that optimization is really helpful and can have a large impact 20:10 < neli> those are used often 20:10 < Almindor> neli, they cannot be sealed 20:10 < neli> but I guess they can't be 20:11 < Almindor> that's the whole problem 20:11 < Almindor> sealing in RTL would be grand help 20:11 < Almindor> but we can't because there's tons of code which probably depends on it 20:11 < fpk> classes in the rtl/fcl are designed to be derived 20:12 < Almindor> xzacly :) 20:12 < fpk> if users don't use it, it's their problem 20:12 < Thorsten__> many classes in user code not... and they can be sealed then 20:12 < Almindor> yes, but I think most people won't, it's just an opinion tho 20:12 < Almindor> in Java I witnessed 2 cases with "final" 20:12 < Almindor> 1. ignorance 20:13 < Almindor> 2. overuse 20:13 < Almindor> sealed will most probably end that way 20:13 < fpk> poeple using java don't care about performance 20:14 < fpk> and: it doesn't apply that much as it applies to php, but most java coders are also not very good coders 20:16 < Thorsten__> class helpers... it's correct that they are only visible to other units if they are in the uses list... but as class helpers can only add methods and properties (no fields) and cannot override any methods there is no change in behavior of the class itself... the only difference is that the additional methods are there or not.... and yes, borland had a specific purpose in mind for them when they were introduced, making .net System.Object look like TObje 20:16 < Thorsten__> ct (ClassName, ClassType, .. the standard methods of TObject) 20:17 < Thorsten__> but just because that's the original purpose of them doesn't mean that's the only case where they are useful 20:17 < Thorsten__> e.g. for .. in depends on a method called GetEnumerator 20:18 < Thorsten__> suppose you have some external objects you are using that you want to use in for .. in but which don't implement GetEnumerator 20:18 < Thorsten__> you can simply use a class helper to add that without touching the original code 20:18 < oliebol> fpk: You could seal tstringlist by using the "TCustomStringList" trick. 20:19 < Almindor> IMHO all these OOP "enhancements" are result of wrong OOP design in the first place (not just pascal, all class based languages) 20:19 < oliebol> Thorsten: I'm sorry, but I don't think you and NexusDB are the good example for optimization. You are the kind that makes it no matter what. 20:20 < Thorsten__> just because not everyone will utilize a specific compiler feature doesn't seem to be a good argument not to implement it? 20:20 < oliebol> I think it is more important, optimizationwise, what you can do without effort, since that affects way more users than the RTL and a few diehards 20:20 < oliebol> Thorsten: I already changed opinion about sealed. 20:20 < Almindor> I think we should concentrate more on stuff like new threading methods 20:21 < Almindor> which IMHO are really required (not just in fpc) 20:21 < oliebol> Almindor: amateurs trying implicit threading like features will get lost in locking hell. 20:21 < oliebol> They might actually run slower ;_) 20:21 < Thorsten__> adding support for final and/or sealed should actually be quite simple. 20:21 < oliebol> Yes. That is also a factor 20:23 < Thorsten__> if you don't like "class helpers" then allow me to conditionally override the . operator ;) different syntax, same effect 20:25 < Almindor> I wish it was possible to implement a prototype based dynamic language without VM or interpreter 20:25 < Almindor> all of the OOP hoopsla of C++ heritage would be "fixed" in a very nice way 20:26 < Thorsten__> or maybe simply something like "procedure Foo(var Self: Integer; ...); method;" that the compiler allows to call as "i.Foo(...);" 20:28 < Thorsten__> "procedure (var Self: Integer)Foo(...);" ? 20:29 * oliebol fails to see the "effect" 20:30 < oliebol> It's use would still have the same limitation. Something would be in the class depending on uses line, which IMHO is evil. [20:30] [oliebol(+ei)] [2:#fpc(+n)] [Act: 1,3] 20:33 < Thorsten__> It becomes very useful the moment you have something like the for .. in construct which depends on certain methods with certain names being implemented on something. For an example read up on extension methods in C# and how they work to support LINQ 20:34 < oliebol> Yes. But that is a hack. ( http://www.interact-sw.co.uk/iangblog/2005/09/26/extensionmethods) http://www.hu.freepascal.org/fpcircbot/cgipastebin?msgid=944 1. type 2. Nullable = record 3. private 4. naValue : T; 5. naHasValue : Boolean; 6. 7. function GetValue: T; 8. procedure SetValue(const aValue: T); 9. public 10. class operator Implicit(From: T): Nullable; inline; 11. class operator Explicit(From: Nullable): T; inline; //explicit because can raise exception 12. 13. function ValueOrDefault(Default: T): T; 14. procedure Clear; 15. 16. property Value: T 17. read GetValue 18. write SetValue; 19. property HasValue: Boolean 20. read naHasValue; 21. end; 22. 23. function Nullable.GetValue: T; 24. begin 25. if not naHasValue then 26. raise ENullableNoValue.Create; 27. Result := naValue; 28. end; 29. 30. procedure Nullable.SetValue(const aValue: T); 31. begin 32. naValue := aValue; 33. naHasValue := True; 34. end; 35. 36. class operator Nullable.Implicit(From: T): Nullable; 37. begin 38. Result.Value := From; 39. end; 40. 41. class operator Nullable.Explicit(From: Nullable): T; 42. begin 43. Result := From.Value; 44. end; 45. 46. 47. function Nullable.ValueOrDefault(aDefault: T): T; 48. begin 49. if naHasValue then 50. Result := naValue 51. else 52. Result := aDefault 53. end; 54. 55. procedure Nullable.Clear; 56. begin 57. naHasValue := False; 58. end; 59. 20:59 < oliebol> Thorsten: Hmm. I don't see why implicit should always be by value, and explicit by ref. 21:00 -!- giantm is now known as giantm-away 21:00 < oliebol> Seems like you need something like weak rather than implicit/explicit ? 21:00 < oliebol> A weak reference I mean 21:00 < Thorsten|NexusDB> explicit is a conversion from the Nullable to T in this case 21:00 < Thorsten|NexusDB> it is used so that you can't just do something like: 21:01 < Thorsten|NexusDB> var a: Integer; b:Nullable; begin if a < b then ... 21:01 < Thorsten|NexusDB> if the conversion from Nullable to Integer is implicit (the only thing that is supported in FPC) then that can easily result in an exception 21:01 < Thorsten|NexusDB> (if b doesn't have a value) 21:02 < Thorsten|NexusDB> having the conversion as explicit means the user is forced to do either: 21:02 < Thorsten|NexusDB> if a < Integer(b) then 21:02 < Thorsten|NexusDB> in which case the user is aware that something may go wrong, or: 21:02 < Thorsten|NexusDB> if a < b.ValueOrDefault(0) then 21:03 < Thorsten|NexusDB> or if you have 21:04 -!- sjc [n=sjc@user-54459267.lns1-c13.telh.dsl.pol.co.uk] has joined #fpc 21:04 < Thorsten|NexusDB> var a: Integer; b: string; begin if a <> b then #... 21:04 < Thorsten|NexusDB> if both of these have an implicit assignment to e.g. variant which then has a compare operator I think FPC will currently do the variant conversion? jonas-fpc> The main problem with sealed/final stuff is that this always also gets used in frameworks, because something "surely will never have to be extended" 21:21 < jonas-fpc> Thorsten|NexusDB: with final classes a developer has to declare a descendent of each class in the package in his app as final. That's just plain ugly (and a lot of tedious work). Marc Weusting (during BBQ): for..in is important because you can walk over a set defined with an anonymous enum. (IIRC he pretty much implied the other users were irrelevant). FPC can work around this due to a different extension (high and low() work on sets), but needs a bit more typecasts. OTOH, if you need the loopvar, this can be only done using FPC style and not using for..in?