{"id":244,"date":"2008-08-25T22:32:53","date_gmt":"2008-08-25T10:32:53","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=244"},"modified":"2008-08-25T22:51:19","modified_gmt":"2008-08-25T10:51:19","slug":"generic-methods-and-type-inference","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/244\/","title":{"rendered":"Generic Methods and Type Inferencing"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">7<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>In the <a href=\"https:\/\/forums.codegear.com\/index.jspa\" target=\"_blank\">new Delphi forums<\/a> recently, <a href=\"http:\/\/barrkel.blogspot.com\/\" target=\"_blank\">Barry Kelly <\/a>responded to a question about lambda expression syntax in Tibur\u00f3n with this observation:<\/p>\n<p style=\"padding-left: 30px;\"><a href=\"https:\/\/forums.codegear.com\/thread.jspa?messageID=3705&amp;#3705\" target=\"_blank\">This syntax needs type inference. Our compiler was not originally written to support type inference, but work to support type inference is<\/a><a href=\"https:\/\/forums.codegear.com\/thread.jspa?messageID=3705&amp;#3705\" target=\"_blank\"> orthogonal to supporting anonymous methods. &#8230;\u00a0 you&#8217;ll need to provide the full declaration type, for now.<\/a><\/p>\n<p>In other words &#8211; as I understand it &#8211; Tibur\u00f3n\/Delphi 2009 will not (initially at least) support type inferencing.\u00a0 To my mind this dramatically reduces the attractiveness of Generic Methods.<\/p>\n<p><!--more--><\/p>\n<h3>What Is Type Inferencing?<\/h3>\n<p>As the term suggests, (in this context at least) it is the ability of the compiler to infer the type of some symbol (variable or parameter etc) from the context or code around it.<\/p>\n<p>In C# for example, it means that variables can be declared:<\/p>\n<pre class=\"csharp\">  var a = 10;<\/pre>\n<p>The compiler will infer from this that the type of variable <strong>a<\/strong> is <strong>int<\/strong> (you knew that, right?) without this having to be explicitly declared.<\/p>\n<p style=\"padding-left: 30px;\"><strong>Pop Quiz:<\/strong> What do we think would\/should the type of <strong>a<\/strong> be if Delphi were to support this?<\/p>\n<p style=\"padding-left: 30px;\"><em>Hint: What type would <strong>a<\/strong> be if it were a constant?<\/em><\/p>\n<p>In general I find this to have dubious value, what, for example, can you deduce about the correct usage of <strong>a<\/strong> from this:<\/p>\n<pre class=\"csharp\">  var a = someRef.DisplayValue;<\/pre>\n<p>To know the type of <strong>a<\/strong> you know need to know the type of <strong>someRef<\/strong> in order to in turn know the return type of <strong>DisplayValue<\/strong>.\u00a0 If browsing a project with all references intact and <em>Code Insight<\/em>, or equivalent, to deliver this information to you, then all good.\u00a0 But if not, you are going to have a sticky time of it, and either way you still have to trust that the person that wrote the code &#8211; which perhaps wasn&#8217;t you &#8211; also knew those things and didn&#8217;t make their own inferencing error.<\/p>\n<p>But what does type inferencing have to do with Generic Methods?<\/p>\n<p>To understand that let&#8217;s look at why Generic Methods even come into it.<\/p>\n<p>It all started this evening when I found myself needing to add another overload to my set of <strong>Exchange()<\/strong> methods, two of which I show here to give you the idea:<\/p>\n<pre class=\"delphi\">  procedure Exchange(var A, B: Integer); overload;\r\n  procedure Exchange(var A, B: TObject); overload;<\/pre>\n<p><em>(The <strong>Exchange<\/strong> procedure exchanges the values of the two passed parameters)<\/em><\/p>\n<p>My problem was that because the parameters to these procedures are, by necessity, <strong>var<\/strong> parameters, the compiler enforces strict type checking, so the second of the above declarations cannot be used with variables of a <strong>TObject<\/strong> <em>derived<\/em> type, but only variables explicitly and specifically of <strong>TObject<\/strong> type itself:<\/p>\n<pre class=\"delphi\">  var\r\n    obj1, obj2: TObject;\r\n    form1, form2: TForm;\r\n  begin\r\n    :\r\n    Exchange(obj1, obj2); \/\/ OK - references will be exchanged\r\n    Exchange(form1, form2); \/\/ ERROR: No compatible overload\r\n  end;<\/pre>\n<p>The second call to <strong>Exchange()<\/strong> will not compile because the <strong>TForm<\/strong> type parameters are not compatible (from the compiler&#8217;s perspective) with the <strong>TObject<\/strong> overloaded version of the routine.\u00a0 I have to add another overload with explicitly <strong>TForm<\/strong> type parameters.<\/p>\n<p>Not for the first time recently I found myself thinking &#8220;If only I had Delphi 2009 &#8211; Generic Methods would make this so much easier!&#8221;.\u00a0 Then I remembered reading Barry Kelly&#8217;s note about lack of type inferencing in Delphi 2009, and an alarm bell started ringing.<\/p>\n<h3>The Generic Solution<\/h3>\n<p>A generic implementation of an <strong>Exchange()<\/strong> procedure should be simple enough, and would go a little something like this:<\/p>\n<pre class=\"delphi\">  procedure Exchange&lt;T&gt;(var A, B: T);\r\n  var\r\n    i: T;\r\n  begin\r\n    i := A;\r\n    A := B;\r\n    B := i;\r\n  end;<\/pre>\n<p>Which is nice and neat and cuts down on all those overloads.  Unfortunately however, <span style=\"text-decoration: underline;\">using<\/span> this procedure is actually now more cumbersome, not less:<\/p>\n<pre class=\"delphi\">  var\r\n    obj1, obj2: TObject;\r\n    form1, form2: TForm;\r\n  begin\r\n    :\r\n    Exchange&lt;TObject&gt;(obj1, obj2);\r\n    Exchange&lt;TForm&gt;(form1, form2);\r\n  end;<\/pre>\n<p>Ouch.<\/p>\n<p>Frankly for the time it takes to create a new overload of a trivial routine like this, I would rather take those few seconds and reap the rewards later since &#8211; in common with most such routines &#8211; the code for the procedure will be written only once but code to call it will be written many, many times.<\/p>\n<p style=\"padding-left: 30px;\"><em>Voices Off:<\/em> &#8220;But lots of overloads will pollute your namespace!&#8221;<\/p>\n<p>You know what?  That bothers me a lot less than creating unnecessary work for myself.  I&#8217;m not the sort of developer that is unable to produce a line of code without invoking <em>Code Completion<\/em>.  I may be unusual in this day and age, but I still <span style=\"text-decoration: underline;\">write<\/span> code faster than I can pick it from drop-down lists.<\/p>\n<p>And this in Win32 Delphi at least is surely the biggest impact?\u00a0 As far as the code goes,\u00a0 any unused overloads will be pruned out by the linker.\u00a0 In .NET &#8211; aiui &#8211; a cluttered namespace becomes a public nuisance, so the imperatives are somewhat different perhaps.<\/p>\n<h3>How Would Type Inferencing Help?<\/h3>\n<p>Well, assuming that any future Delphi type inferencing system could determine the appropriate type from the parameters (as it can in C#) then we could invoke our generic <strong>Exchange&lt;T&gt;()<\/strong> method by simply writing:<\/p>\n<pre class=\"delphi\">  var\r\n    obj1, obj2: TObject;\r\n    form1, form2: TForm;\r\n  begin\r\n    :\r\n   \u00a0Exchange(obj1, obj2);\r\n    Exchange(form1, form2);\r\n  end;<\/pre>\n<p>i.e. just as we can do with overloads.<\/p>\n<p>But until this is possible at this stage I was feeling that I would continue with overloads until &#8211; at least &#8211; type inferencing were available.\u00a0 They are just as type-safe and ironically produce &#8220;consumer&#8221; code that should be entirely compatible with a future generic methods implementation that <em>is<\/em> bolstered by type inferencing.<\/p>\n<p>Using generic methods <span style=\"text-decoration: underline;\">without<\/span> type inferencing (in these sorts of cases at least) will simply create unnecessarily verbose and cumbersome &#8220;consumer&#8221; code.<\/p>\n<p>But not being one to give in, I considered some alternative approaches.<\/p>\n<h3>A Truly generic Approach?<\/h3>\n<p>Alternatively, Delphi already provides a means to implement a truly generic (lower case &#8220;g&#8217;) <strong>Exchange()<\/strong> method &#8211; untyped parameters:<\/p>\n<pre class=\"delphi\">  procedure Exchange(var A, B);\r\n  var\r\n    i: ?\r\n  begin\r\n    i := A;\r\n    A := B;\r\n    B := i;\r\n  end;<\/pre>\n<p>You will immediately notice of course that this implementation is neither valid nor complete.  The type of <strong>i<\/strong> (for &#8220;intermediate&#8221;, if you were wondering) is not known and indeed not knowable.\u00a0 And furthermore, the compiler simply won&#8217;t accept that <strong>A := B<\/strong> assignment  since it doesn&#8217;t know the types of <strong>A<\/strong> and <strong>B<\/strong> it cannot know what instructions are needed.\u00a0 For the same reason, <a href=\"http:\/\/en.wikipedia.org\/wiki\/XOR_swap_algorithm\" target=\"_blank\">the XOR trick<\/a> won&#8217;t work either.\u00a0 Those untyped parameters seem to have lead us to a dead end.<\/p>\n<p>Not quite.\u00a0 (If you are squeamish and\/or don&#8217;t like smelly code you might want to avert your gaze about now, or at least pinch your nose):<\/p>\n<pre class=\"delphi\">  procedure Exchange(var A, B);\r\n  var\r\n    aa: Integer absolute A;\r\n    bb: Integer absolute B;\r\n    i: Integer;\r\n  begin\r\n    i  := aa;\r\n    aa := bb;\r\n    bb := i;\r\n  end;<\/pre>\n<p>The <strong>absolute<\/strong> keyword is not something to be used lightly &#8211; it tells the compiler that the variables <strong>aa<\/strong> and <strong>bb<\/strong> exist at the same location as the parameters <strong>A<\/strong> and <strong>B<\/strong> &#8211; in this case the compiler even takes care of the fact that the parameters are passed by reference.<\/p>\n<p>Since the variables are typed and the parameters are untyped, how can this be safe?<\/p>\n<p>Well, frankly it isn&#8217;t.<\/p>\n<p>In practice if the type of <strong>A<\/strong> and <strong>B<\/strong> is not the same size as an <strong>Integer<\/strong> (32-bits) then things are not going to go at all according to plan.<\/p>\n<p>Equally of course though, if you only ever call this implementation routine with 32-bit sized parameters (which includes strings, object references etc) there won&#8217;t be a problem.\u00a0 &#8220;IF&#8221;.<\/p>\n<p>I should point out at this stage that I explored this approach as a curiosity.\u00a0 I am certainly NOT recommending it!\u00a0 For one thing it is not &#8220;truly generic&#8221; at all &#8211; it only appears to be but in fact has fairly strict conditions for correct use, and does not benefit from any assistance from the compiler to ensure that you do in fact use it correctly!<\/p>\n<p>But we have one more trick up our sleeve &#8211; there is (at least) one more way to skin this particular cat that is safer than an untyped parameter approach and only a little more cumbersome to use than a non-type-inferenced Generic approach.<\/p>\n<h3>X-Rated Code &#8211; Being Explicit<\/h3>\n<p>A <strong>var<\/strong> parameter is syntactic sugar for passing by referencing and allowing modification of the de-referenced value.\u00a0 We can of course achieve the same thing by taking care of the de-referencing aspects ourselves and explicitly passing references to values, rather than the values themselves:<\/p>\n<pre class=\"delphi\">  type\r\n    PObject = ^TObject;\r\n\r\n   :\r\n\r\n  procedure Exchange(const A, B: PObject);\r\n  var\r\n    i: TObject\r\n  begin\r\n    i  := A^;\r\n    A^ := B^;\r\n    B^ := i;\r\n  end;<\/pre>\n<p>And to call this:<\/p>\n<pre class=\"delphi\">  var\r\n    obj1, obj2: TObject;\r\n    form1, form2: TForm;\r\n  begin\r\n    :\r\n   \u00a0Exchange(@obj1, @obj2);\r\n    Exchange(@form1, @form2);\r\n  end;<\/pre>\n<p>All of which sits happily alongside any other overloaded versions of <strong>Exchange()<\/strong>, but which unfortunately requires consumer code that will not be compatible with a future Generic Method implementation of the routine.<\/p>\n<p>BUT, even this won&#8217;t work if we are compiling with <strong>Typed @ Operator<\/strong> option enabled.<\/p>\n<p>So I&#8217;m left a little stumped.<\/p>\n<p>There are many ways to go about this.\u00a0 If it weren&#8217;t for the fact that Generic Methods are nearly upon us I would favour the explicit de-referencing approach.\u00a0 But the desire to create code today that will be compatible with impending new language features is quite compelling.\u00a0 Then again, until we also get type inferencing, a Generic Methods based approach isn&#8217;t going to be compatible with <span style=\"text-decoration: underline;\">anything<\/span> we can write today anyway.<\/p>\n<p>Despite myself, the untyped parameter approach could yet prove too tempting to resist (particularly if I find myself needing yet another class-specific version of <strong>Exchange()<\/strong>) since all the types that I&#8217;ve ever found myself wanting to <strong>Exchange()<\/strong> meet the 32-bit criteria.<\/p>\n<p>It really hinges on when we might see type inferencing in a Delphi compiler.\u00a0 If that is something we are likely only to see in <a href=\"http:\/\/dn.codegear.com\/article\/36620\" target=\"_blank\">Commodore<\/a> then worrying about compatability of a few calls to Exchange() could prove somewhat misplaced given the far wider issues likely to arise from the move to a 64-bit compiler.<\/p>\n<h3>A Compromise?<\/h3>\n<p>In the meantime I wonder whether it would not be possible to have a compiler option to disable such strict type checking on var parameters, ideally on a method-by-method basis.<\/p>\n<p>After all, we still have such an option for short string var parameters (although having never had cause to use it I don&#8217;t know if it works in quite the way I have in mind &#8211; i.e. on declarations of rather than calls to, methods).<\/p>\n<p>In the case of class-type var parameters the more relaxed type checking could allow parameters of any type correctly derived from the formal type:<\/p>\n<pre class=\"delphi\">  interface\r\n    {$VARSTRICTCLASSTYPE OFF}\r\n    procedure Exchange(var A, B: TObject);\r\n    {$VARSTRICTCLASSTYPE ON}\r\n\r\n  implementation\r\n\r\n    procedure Exchange(var A, B: TObject);\r\n      :\r\n    end;<\/pre>\n<p>Then again, I can&#8217;t think of any other concrete examples where such a capability would have any practical use.\u00a0 But if this were possible to implement more quickly and safely than a comprehensive type inferencing system, I think I&#8217;d take it to keep me going.<\/p>\n<p>\ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">7<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> In the new Delphi forums recently, Barry Kelly responded to a question about lambda expression syntax in Tibur\u00f3n with this observation: This syntax needs type inference. Our compiler was not originally written to support type inference, but work to support type inference is orthogonal to supporting anonymous methods. &#8230;\u00a0 you&#8217;ll need to provide the full [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[4,7],"tags":[292,21,47],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-3W","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":1882,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1882\/","url_meta":{"origin":244,"position":0},"title":"Not Your Grand-Daddy&#8217;s Pascal (or Java)","date":"15 Oct 2013","format":false,"excerpt":"I've mentioned some of the cool stuff in the Oxygene language in various posts and thought it would be a good idea to list them again, along with some others that I've not previously mentioned. Oxygene Everywhere First some of the core language features that are available on all supported\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":254,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/254\/","url_meta":{"origin":244,"position":1},"title":"An Exchange() For All","date":"26 Aug 2008","format":false,"excerpt":"Following on from yesterday's post, Barry Kelly (CodeGear engineer) kindly clarified a few points, one of which was that Generics support in Delphi 2009 won't extent to unit procedures, only class methods, so speculation about a possible generic implementation of a Swap()\/Exchange() routine was rendered largely academic. Not to be\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":371,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/371\/","url_meta":{"origin":244,"position":2},"title":"Delphi 2009 &#8211; CompilerVersion","date":"19 Sep 2008","format":false,"excerpt":"Just a short post this one. Somebody else may already have mentioned this, but I only just figured it out for myself - the CompilerVersion for the Delphi 2009 compiler is not what you might expect (and is not what the documentation says it is!). Both CompilerVersion and RTLVersion have\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":180,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/180\/","url_meta":{"origin":244,"position":3},"title":"Latest Tibur\u00f3n Preview","date":"14 Aug 2008","format":false,"excerpt":"I was a little disappointed that the preview webinar this morning was little more than a re-run of the same content from a little over a week ago, albeit with some downloadable PowerPoint slides this time. It was at least an opportunity for some more Q&A and a couple of\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1207,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1207\/","url_meta":{"origin":244,"position":4},"title":"Adventures in Syntax: Something Old, Something New etc&#8230;","date":"20 Sep 2012","format":false,"excerpt":"As the post title says, this will be a brief detour through some features of the Pascal language and a presentation of some (theoretical) alternatives that could have been introduced instead. That is, some are real but little known syntax, others are what I think might be preferable to the\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1300,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1300\/","url_meta":{"origin":244,"position":5},"title":"What&#8217;s in a Name(space) ?","date":"28 Nov 2012","format":false,"excerpt":"The ever evolving DWScript project continues to advance the Pascal language at an impressive pace. Just today it was announced that this scripting version of Pascal now has \"namespace\" support. When I first read the details of the implementation, my initial reaction was that it \"felt a bit backwards\". The\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/244"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=244"}],"version-history":[{"count":9,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/244\/revisions"}],"predecessor-version":[{"id":253,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/244\/revisions\/253"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=244"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=244"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=244"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}