[Estimated Reading Time: 9 minutes]

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 platforms – Java, Cocoa and .NET

  • Property Members
  • Property Read Expressions
  • Properties with Different Read/Write Protections
  • Sophisticated Class Member Scoping
  • if-expressions
  • Extended Constructor Calls (Create and Initialise)
  • Case Statements with Strings
  • In-line Variable Declarations
  • Variable Type Inferencing
  • Colon Operator

Property Members

If you have a simple property which directly exposes a member variable you do not need to formally declare the member variable and the accessor/mutator properties, you can just declare the member variable as a property:

  Rectangle = class
  public
    property Width: Integer;
    property Height: Integer;
  end;

Property Read Expressions

If you have a property whose value can be expressed using other members of the class you don’t need to wrap this up inside a formally declared accessor method, you can just write the expression directly in the property declaration:

  Rectangle = class
    ..
    property Area: Integer read Width * Height;
  end;

Combining property members and read expressions allows us to write a simple rectangle class very cleanly, without any implementation code required at all:

  Rectangle = class
  public
    property Width: Integer;
    property Height: Integer;
    property Area: Integer read Width * Height;
  end;

Which compares very favourably indeed with grand-daddy’s Pascal:

  TRectangle = class
  private
    fWidth: Integer;
    fHeight: Integer;
    function get_Area: Integer;
  public
    property Width: Integer read fWidth write fWidth;
    property Height: Integer read fHeight write fHeight;
    property Area: Integer read get_Area;
  end;

  
  function TRectangle.get_Area: Integer;
  begin
    result := fWidth * fHeight;
  end;

Properties with Different Read/Write Protections

Ever had a property which was read-only for public access but which you wanted to make writable for private or protected use ? With Oxygene you can. In this case however, you do need to declare the accessor/mutator methods or fields. The property has a default visibility “inherited” from the visibility specified applying to the declaration, but this can be “overridden” on the read/write accessors.

So to create a public readable property with protected write access:

  Rectangle = class
  private
    fWidth: Integer;
    fHeight: Integer;
  public
    property Width: Integer read fWidth protected write fWidth;
    property Height: Integer read fHeight protected write fHeight;
    property Area: Integer read Width * Height;
  end;

Sophisticated Class Member Scoping

Oxygene has some very expressive capabilities in the area of member scoping.

In addition to the usual private, protected, public, strict private, strict protected etc, Oxygene also allows assembly (corresponding to “package” in Java) and unit scoping.

The possible combinations are too numerous to mention here so for a full breakdown I refer you to this page of the Oxygene Language Wiki.

Suffice to say that the ability to directly express (and enforce) that a member be accessible to only those classes in a unit which are also sub-classes of the class or, alternatively, accessible to classes in a unit and to any sub-classes (to name just two possibilites) is very useful.

if-expressions

Anywhere that an expression may be used, an if-expression can be employed as an inline expression selector so instead of:

  if useRed then
    view.TextColor := Color.RED
  else
    view.TextColor := Color.WHITE;

We can write:

  view.TextColor := if useRed then Color.RED else Color.WHITE;

Extended Constructor Calls

Very often in OO we write code that consists of creating some object then setting some properties on that object:

  myButton := new Button(self);
  myButton.Caption := 'OK';
  myButton.Width   := 100;
  myButton.Height  := 25;

To simplify such cases in Oxygene, property assignments can be appended to the parameter list of any supported constructor. The initial parameters will be used to identify and call the appropriate constructor before applying the additional assignment expressions:

  myButton := new Button(self, Caption := 'OK', 
                               Width   := 100,
                               Height  := 25);

Case Statements with Strings

Oxygene case statements have much broader application than in Delphi. The most notable difference being that they support string selectors:

  case aIntent.Action of
    'nz.co.deltics.SCREEN_ON' : ... ;
    'nz.co.deltics.SCREEN_OFF' : ... ;
  else
    ...
  end;

I seem to recall in an early version of Oxygene (what was then known as “Chrome”), the else clause of a case statement required a ‘begin’. It was arguably more syntactically correct according to a strict application of core Pascal syntax, but it was a bit cumbersome and somewhere along the way this seems to have changed. πŸ™‚

Inline Variable Declaration

Variables can be declared in the usual Pascal manner in Oxygene, within a var block preceding the begin statement of a method. Or you can declare a variable in-line at first use. In-line declarations have the scope of their containing statement block.

  if bSomeCondition then
  begin
    var i: Integer;

    ... do some work with an Integer, i
  end
  else
  begin
    var i: Intent;

    ... do some work with an Intent, i
  end;

  if NOT assigned(i) then   // Compilation fails.  "i" does not exist
    EXIT;

Inline Variable Type Inference

Most often when declaring a variable inline, it will be immediately initialised. In such cases Oxygene does not require that you declare the variable type. It will infer the appropriate type from the assignment expression. So the above in-line declaration example with some assumed initialisation, might look like this

  if bSomeCondition then
  begin
    var i := 42;

    ... do some work with an Integer, i
  end
  else
  begin
    var i := new Intent(Intent.ACTION_POWER_CONNECTED);

    ... do some work with an Intent, i
  end;

Colon Operator

The colon operator is a variation on the dot operator. Unlike the dot operator however, the colon operator offers some protection against potentially nil references. This can make certain programming tasks far easier.

Consider this hypothetical example of looking up some user object by name, obtaining the details object for that user and a company object within it and then extracting the name of that company, where there may not be any such user, the user may not have any details and the details if provided may not identify a particular company.

In terms of runtime performance, the most efficient way to implement this would be:

  sCompanyName := '';

  user := fUsers.getUserByName(aUserName);
  if Assigned(user) then
  begin
    var details := user.Details;
    if Assigned(details) then
    begin
      var company := details.Company;
      if Assigned(company) then
        sCompanyName := company.Name;
    end;
  end;

This example takes advantage of in-line, scoped variable declarations and type inferencing to improve things at least as far as possible. The equivalent in Delphi would be even more messy.

It is however unlikely that you would find such attention being paid to runtime efficiency given the aversion to “premature optimisation” and unnecessary typing that seems so prevalent these days. That, and the lack of in-line variable declarations and type inferencing means that you would be far more likely to encounter something like:

  user := fUsers.getUserByName(aUserName);
  if Assigned(user) and Assigned(user.Details) and Assigned(user.Details.Company) then
    sCompanyName := user.Details.Company.Name
  else
    sCompanyName := '';

You will notice that in the case where there is a valid Company.Name to be retrieved, the above code evaluates user.Details no less than 3 (three!) times. If you are lucky, neither Details nor Company will be relatively expensive property accessor functions. Of course, they might not be when this code is first written, but that could change.

The Oxygene colon operator allows us to write code that at runtime is exactly equivalent to that first example however, but to do it in a single line of code:

  sCompanyName := fUsers.getUserByName(aUserName):Details:Company:Name; 

Not Your Grand-Daddy’s Java

So much for these additions to Pascal. However, there are also some things that we are well used to in Pascal – especially Delphi – that at first blush at least appear to be missing in Java.

Fortunately, Oxygene can ease the transition by adding support for some of these itself.

  • get/set Methods as Properties
  • Class References
  • Virtual Class Methods and Constructors
  • By-Reference Parameters and Records

get/set Methods as Properties

Java has no formal concept of properties. Instead the language has adopted a pattern based approach adopted from Java Beans, settling on the use of getPropertyName() and setPropertyName().

Oxygene recognises this pattern and exposes methods (which fit) as properties.

  var i := new Intent;

  i.Action := Intent.ACTION_POWER_CONNECTED:  // i.setAction( Intent.ACTION_POWER_CONNECTED );

Not all such methods fit this pattern. If a get..() method has a void result (does not return a value) for example, then it is not a property accessor and cannot be treated as such.

This is purely syntactic sugar, however. You can still use the formal Java methods directly if you wish (or if the parameter lists do not suit a property style invocation).

Class References

Java has a Class type and every Object has a Class member which exposes the underlying Class type. But (from what I understand) this is not quite as capable* as Delphi’s class references concept, at least not without a great deal more work on the part of the developer.

*: In one area, Java class types serve a purpose which Oxygene class references do not (currently?) appear to. That is, to serve as class identities, as discussed in this stackoverflow question.

Fortunately Oxygene implements Delphi-like class references for us as well.

Consider the following class with two constructors, one parameterless and one accepting an integer parameter:

  type
    Foo = public class
      constructor;
      constructor(aID: Integer);
    end;
    FooClass = class of Foo;

Using the FooClass class type, the declaration of which will be familiar to any Delphi developer, we can do everything we would expect. We can declare class reference parameters and use those references to create instances of the referenced class using any of it’s supported constructors:

  method Bar.createFoo(aFooClass: FooClass; aID: Integer): Foo;
  begin
    result := new aFooClass(aID);
  end;

This is possible in Java (it must be, given that Oxygene compiles to the JVM byte code) but is far more complex, involving reflection if you need to invoke anything other than parameterless constructors.

I suspect that the Oxygene class reference implementation is founded on these underlying capabilities of Java, but surfaces them in a far more usable fashion!

  var
    fc: FooClass;
    jc: &Class<Foo>;
    f: Foo;
  begin
    fc := FooClass;   
    f := new fc(42);  

    // Two ways of obtaining the Java Class type, from a class and from an object:

    jc := typeOf(Foo);      // equivalent of ' = Foo.Class' in Java
    jc := f.&Class;

    // Instantiating a new class using Java Class type:

    f := jc.newInstance;
    f.setID(42);
  end;

Note: Since the Java Class mechanism uses Class as an identifier, thus clashing with the class keyword in Oxygene, use of the Java Class identifier has to be qualified with the identifier prefix ‘&‘, just as we would in Delphi in such cases.

Virtual Class Methods, Including Constructors

The real power of class references in Delphi stems from combining them with virtual class methods and virtual constructors. And yes, by some wizardry, these too are supported by Oxygene for Java !

Given these hypothetical declarations of an Employee and a Person class:

  Person = public class
  public
    constructor; virtual;
    class method getMessage: String; virtual;
  end;
  PersonClass = class of Person;


  Employee = public class(Person)
  public
    constructor; override;
    class method getMessage: String; override;
  end;

The details of the constructors and the getMessage methods aren’t important, other than that the Employee constructor must obviously call inherited while in the case of getMessage this is not important and we simply return a different string from the version of this method on each class allowing us to easily determine which class was invoked.

We get the expected results from this sort of code:

var
  fc: FooClass;
  f: Foo;
  s: String;
begin
  fc := Bar;
  f := new fc;

  s := f.Class.Name;    // yields = 'Bar'

  s := f.getMessage;    // calls Bar.getMessage
end;

One thing to note here however is that type inferencing does not work for variables that are class reference types. I am not sure if this is by design, an unavoidable consequence of the implementation details, or whether it is simply a bug. Either way, it’s not a huge problem.

I have not looked to see how a class reference or virtual constructors such things appear to a Java developer consuming an Oxygene class.

With A Little Help From My Friends

There are a couple of further things that Oxygene adds to Java but which rely on an additional RTL support package (a jar file) called com.remobjects.oxygene.rtl.

By-Reference Parameters and Records

In Java, everything passed as a parameter to a method call is always, always passed by value. There are no by reference parameters in Java (var parameters in Pascal/Delphi speak).

Unless you are using Oxygene.

  BatteryUtil = public class
  public
    method getBatteryInfo(var aPercent: Integer; var aCharging: Boolean);
  end;

If you use var parameters and your project does not reference the required RTL library you will be prompted to add it by a compiler error.

You will get a similar prompt to add the same library (if you are not using it) if you use another language feature that you may be used to in Pascal/Delphi: records.

Java has no specific support for record types or (structs, if you prefer). But Oxygene does. Again, via the RTL support library.

Is There NOTHING That Oxygene Can’t Do ?

It might seem like that, but actually there is (at least) one thing.

If you look at the extensions to the Java language capabilities that Oxygene provides, they all rely on encapsulating existing techniques in the JVM for achieving things that other languages support by first class means, elevating these capabilities to first class status in Oxygene itself, without breaking the fact that underneath it all sits Java itself.

Ironically, there is (at least) one area where Java is so much more capable than the Pascal language the an existing first-class capability of Pascal appears not to be so easily portable to Java.

Enums can be declared just as in Pascal, but set type declarations are not supported.

The only reason I can think of for this omission is that Java already has comprehensive support for sets in it’s collections support. As such there is perhaps not one, definitive, “set” type onto which a simple “set of” declaration can be usefully mapped. Any Java class that an Oxygene developer may need to work with might have chosen some other set type.

Perhaps RemObjects have in mind to provide a more versatile syntax to surface the Java set types in the language and are holding off on implementing this until they are sure they have a comprehensive solution. Or perhaps they simply feel there is no need.

Either way, in the meantime declaring a set using the Java framework classes is not much different than a first class “set type” would provide. You will also not encounter any Pascal/Oxygene sets in any Java classes you work with, so the omission is not likely to pose a problem, other than if you wished to use a Pascal set type in some cross-platform, portable Oxygene code. On that score, the NSSet (and NSMutableSet) type on Cocoa is similarly quite different to the Pascal type, again making a language-level mapping less useful than simply providing first-class access to the platform framework capabilities.

34 thoughts on “Not Your Grand-Daddy’s Pascal (or Java)”

    1. This is another instance when you would need to specify an accessor:

        private
          fWidth: Integer;
        public
          property Width: Integer read fWidth;
      

      Which makes sense if you think about it. If the property declaration were the only existence of the member and declared it as read-only then nothing would be able to write to it, making the member entirely useless.

      Type inferencing should be possible and would be nice for properties, but isn’t supported (yet ?).

        1. Think about it… if they did that you would be declaring a property that is only and absolutely read-only. That is, with literally no way at all to set or modify it’s value. Which would be pretty useless.

          It would perhaps be useful if you could do:

          public
            property Width: Integer read private write;
          

          But it also looks pretty confused (not confusing, just confused).

  1. I tried Oxygene recently. I really want to like it. But their apparent policy of a ‘clean break’ from the Delphi languages means I’d effectively have to re-write virtually my entire code base if I wanted to port an app over. Not every line, but I’d certainly have to go through it thoroughly. And that defeats the purpose for me. If I’m going to have to re-write an app to get it on android, I might as well re-write it in Java.

    1. If the purpose is to port your code verbatim then neither Java nor Oxygene will suit. Concluding that either one is a better choice than the other on that basis is flawed reasoning.

      In terms of valuable skills, knowledge of the language is less important than knowledge of the platform, and on that score – again – there is nothing to choose between Oxygene for Java and Java itself (nor for that matter between Oxygene for Cocoa and Objective-C or Oxygene for .NET and C#).

      So the question ultimately boils down to which language and which IDE you prefer. And if you are considering supporting multiple mobile platforms, or even only one mobile platform in addition to a desktop platform, productivity is surely going to benefit from being able to use the same IDE, never mind that there will be some code that can be shared across those platforms even if not the 100% that you might ideally like, which choosing Objective-C, Java and C# absolutely rules out.

      Ultimately it’s horses for courses. I simply question the logic behind your conclusion that you “might as well” re-write in Java.

      There are good reasons for choosing Oxygene. Even language preference is a good reason, though I accept it may not be a compelling enough reason for everyone. πŸ™‚

      1. Like I said, I like the idea behind Oxygene. But as a Delphi programmer, that Oxygene looks like Delphi doesnt seen to help much in porting Delphi apps. They’ve made it quite clear that the idea of sharing a code base between Delphi and Oxygene is far down at the bottom of their priorities.

        The point of saying I might as well port to Java is if that much effort is going to go into rewriting, I might as well be using the truly native development environment for the platform and have more development tool options (I dont particularly like VS). I’d just as soon avoid another niche tool.

    2. >I’d effectively have to re-write virtually my entire code base if I wanted to
      >port an app over. Not every line, but I’d certainly have to go through it
      >thoroughly.

      Isn’t that true of any language though? And is “go[ing] through it thoroughly” really that much of a price to pay? Choose a language that has historically been sole-vendor proprietary, Windows-only, single framework only, single IDE only and this is the kind of problems you can expect down the line.

      > And that defeats the purpose for me.

      It all depends on what your purpose is. Writing new code in Oxygene and leaving old code in Delphi would fit a lot of people’s purposes too.

      >If I’m going to have to re-write an app to get it on android, I might as well
      >re-write it in Java.

      You’ll have a lot more work porting it to Java than porting it to Oxygene.

      Honestly there doesn’t seem to be anything really breaking between the two languages. Oxygene appears to have new features; it doesn’t appear to be lacking old ones. It shouldn’t be too complex to write a code generator to port from Delphi to Oxygene, and even less so if instead of porting it merely highlighted trouble areas.

      1. Oxygene have “Paste Delphi as Oxygene” feature, so it can helps in converting code. Yes, all UI/Platform related stuff should be created from scratch (same for every new platform) – but it’s all for good: only specially crafter platform UI can feel & behave like native platform!

        1. If all platform stuff should be created from scratch, why then shouldn’t I create it from scratch using the native development language? Without code reuse, the only thing I’m getting is syntax familiarity. And I get that at the cost being stuck in yet another niche product.

          I dont actually buy the dogma that each platform’s UI must be code separately. Titanium, for example, uses native widgets but has abstract interfaces to make code portable.

          1. True, with Oxygene you get zero UI code re-use but the extent to which you achieve re-use in non-UI, non-platform specific areas is almost entirely up to you. It occurs to me that it might even be possible to devise an abstraction that would enable you to construct an UI programmatically using an abstraction framework. But even if possible it doesn’t exist (for Oxygene) yet, and could only ever be a lowest-common denominator framework. And on mobile platforms users appreciate the specific capabilities of their chosen devices. It’s why they chose one over the other in the first place. So a “one size fits all” solution is not as appropriate on mobile as it might be on the desktop or the web, for example.

            But going back to the lack of re-use…

            If you are developing for Android and iOS using Java and Objective-C respectively you get zero code re-use out of the box and zero potential code re-use going forward.

            Oxygene is a “niche product” ? What is Titanium ?

            You keep drawing out arguments for rejecting Oxygene, but those same arguments apply to the alternates that you seem to suggest your thinking leads you to.

            It’s OK to simply say “I just don’t like it” or “it’s not for me“. But if you are rejecting it based on some reasoning then that reasoning has to add up not just in relation to your decision to not use Oxygene but also in relation to whatever alternate choice you do make. So far I have to say it doesn’t seem to. πŸ™‚

  2. 1. Property Read Expressions
    code in the declaration part? no no. big fail.

    2. if-expressions
    inlined if-expressions fail clear readability. we may end up adding regexps as well. big fail.

    3. Case Statements with Strings
    yeah baby. we need that.

    there is a need in updating language features as oxygene does, but not for the sake of readability and clear code design.

    1. Property Expressions are not “code” in declarations, just expressions. Absolutely no different than any constant declaration that is not a simple alias for some value:

        const
          MILLISECOND = 1;
          SECOND      = 1000 * MILLISECOND;
          MINUTE      = 60 * SECOND;
      

      Are declarations such as these for SECOND and MINUTE also verboten in your world ? Because they consist of “code in the declaration” ?

      I thought not. πŸ˜‰

      How you conclude that if-expressions fail clear readability eludes me entirely. When used as an expression selector it makes it very clear that the conditional part of a statement is not the entire statement but only one expression within it. It makes the intent in the code explicit and eliminates unnecessary duplication.

      True, both could be used badly, but that is true of any language feature. The gamut of supported language syntax is just a toolbox. You know what they say about people who blame their tools. πŸ˜‰

      1. expressions in the declaration part : does a change in the expression cause a break in the declaration, involving dependent declarations etc?

        1. I’m not sure I follow.

          A change in the expression could only “break the declaration” if the expression you change it to isn’t valid, in exactly the same way that an invalid expression in a constant declaration will break the constant declaration and an invalid expression in a property “get” method will break the method.

          In all cases, the result will be compilation failure due to the invalid expression.

          What am I missing that is cause for concern ?

          1. Imagine the day both Oma and Deltics have the source code to the unit defining TRectangle. But we may not have all source code of units depending on unit_rectangle. Can I change the source code of the implementation (in the declaration of the area property) and still use the dcu’s of the depending units?

            Always? Sometimes? Never? You cannot do that if it is in the implementation either? None of the above?

            Is a declaration still a “contract”

            1. The ‘contract’ is the existence of a property with a given name and type. The read accessor is not part of the contract.

              I haven’t looked into it specifically, but since Java (to take one example) does not support properties at all, let alone properties with read-expressions, the compiler must be generating an accessor method behind the scenes. The ability to eliminate the boiler-plate required for those cases where the accessor method can be represented by a simple expression is just syntactic sugar.

              As for the impact on “dcu’s of dependent units” – there are no dcu’s in Oxygene. πŸ˜‰

              A consumer of the Rectangle class is either compiling against the source of Rectangle or is linking to an assembly or JAR etc containing an already compiled Rectangle class. Either way, all the dependent code is dependent upon is that there is a property of the expected name and type.

              I’m not sure where your concern stems from, other than perhaps a misunderstanding. πŸ™‚

      2. these are constants. your example uses properties/variables:

        property Area: Integer read Width div Height;

        what happens if Height = 0? (I can’t verify that because I don’t have oxygene yet). If you try this in a const declaration you’ll get a division by zero on compile time.

        1. Property expressions are not evaluated at compile time any more than property accessor functions get called at compile time in Delphi (you didn’t think they were. did you?).

          The expression is compiled, not evaluated.

          The behaviour is exactly identical to the situation where you formally declare an accessor function and associate it with a property, it simply removes the need to add all the attendant boiler-plate code. It’s just that the compiler does all that donkey work for you. πŸ™‚

          1. ah thanks for clarification: it does produce compiled code (not evualated consts). So I may well repeat again: code in the declaration part? no no. big fail. πŸ™‚

              1. thanks for the invitation, so one more time: code in the declaration part? no no. big fail

              2. One more time from me also…. Perhaps you could explain the difference between these two for me:

                1:

                property Width: Integer read get_Width;
                
                function TRectangle.get_Width: Integer;
                begin
                  result := fWidth;
                end;
                

                2:

                property Area: Integer read get_Area;
                
                function TRectangle.get_Area: Integer;
                begin
                  result := Width * Height;
                end;
                

                What is the substantive difference that makes replacing the accessor function in example 1 with a read expression but makes the case in example 2 so unacceptable ?

                The only difference is in the complexity of the expressions, one involving only one identifier, the other 2 identifiers and an operator which makes it look more like “code” than the other, but both are – in essence – code.

                  property Width: Integer read fWidth;              // 'code' = trivial expression, so that's OK
                  property Area: Integer read Width * Height;       // THE END OF THE WORLD AS WE KNOW IT !!!!
                

                πŸ˜€

              3. first one uses a lot of more code (you’re right), latter one uses code (no,”read fWidth” is not code) in the declaration part. It is shorter of course and more readable (because of shorter code), but now add this property Ratio: Extended read Width / Height; – first look: great, but you don’t have a chance of avoiding an exception if Height is 0. I would write property Ratio: Extended read getRatio;
                function getRatio: Extended;
                begin
                if Height = 0 then
                Result := 0
                else
                Result := Width / Height;
                end;

                oh or like this:

                function getRatio: Extended;
                begin
                Result := if Height = 0 then 0 else Width/Height;
                end;

                or

                property Ratio: Extended read if Height = 0 then 0 else Width/Height;

                is this still readable? reasonable?

              4. The first thing to note is that it’s merely a simple example to demonstrate the language feature.

                The second thing to note of course is that you have had to change the example to demonstrate it’s potential to break. The original example (Area) does not even involve division, so there is no possibility of a division by zero error in an Area expression.

                So again, what is the substantive danger/complaint about that Area expression ?

                Your point about not being able to handle exceptions in cases where exception might occur is of course valid, although the “problem” is predicated on a specification that requires the Rectangle class to handle such exceptions. It is perfectly valid for the Rectangle class to not handle such an exception and for consumers of the class to be responsible for this, in which case a get_Ratio method would also not handle the exception and the consumer code would be identical, whether using an accessor method or a read expression:

                try
                aspectRatio := rect.Ratio

                except
                on EDivideByZero do
                // consumer does what they need to handle a zero height rectangle – could be an error by the user
                end;

                There are cases where the argument would be that this is the more appropriate approach.

                But even if you have a case where the class must handle the exception then yes, that would be a reason to not use the feature in that case, but it is not a reason to rule out it’s use entirely. That case still hasn’t been made. πŸ˜‰

              5. good point with the exception handling (well, my code was just an example – like yours πŸ˜‰
                we’re here working with very strict code rules – I would have a problem expanding those to allow this type of declaration: “you could use this *but only* if it does not raises exceptions; you may use this but not like that, etc. etc.”.
                We’d rather write some more lines of code than running into difficulties just because one of our coders didn’t had enough foresight of what could happen with that type of declaration (yes, we do have some novice coders as well ;-). So feel free to find this type of declaration useful – we won’t πŸ˜‰

              6. That seems to me like a constructed argument.

                There are any number of aspects of any language, especially in an unmanaged environment, where certain techniques and patterns are potentially dangerous unless used “properly”.

                The exact same problem about exceptions applies even if your novice programmers write TRectangle.get_Area() without remembering to handle the zero division exception. Your only defence against those sorts of mistakes – other than “perfect developers” πŸ™‚ – is code review, and a code review will just as easily identify problems with property expressions as it will identify the exact same issues in “long hand” property access methods.

                And ime, the less “noise” there is, the more likely such mistakes are to be identified.

                Also, there is a tendency to make assumptions such as if a “try” immediately follows the construction of an object, it has been known – even during a code review – for the code reader to simply assume that the “try” is part of a “finally” construct to free the temporary object without actually checking. Where-as the lack of a “try” will result in scrutiny of the code to ensure that the object is free’d (or otherwise not free’d) correctly.

                “safe coding patterns” can encourage complacency.

                They say that “To err is human. To really screw things up takes a computer“, but it’s programmers who make computers do what they do, so actually, to really screw things up just takes a certain kind of human. πŸ˜‰

    2. 1. Deltics addressed this better below, but I’d add that lambda expressions are one-line functions that aren’t much different and are in wide use today.

      2. If expressions don’t fail clear readability. They’re present in Python, widely regarded as one of the easiest languages to read and learn, with the nickname “executable pseudocode”. They’re no less clear than an if statement, and often clearer.

      The first time I realized I could use one to make things clearer was when fiddling with Python and a score ranking algorithm in which the denominator changed depending on whether the score to be ranked was the highest or not. At first, given I was almost exclusively a Pascal programmer for most of my time programming, I used an if statement and repeated the formula with the slight change (we sure are used to repeating ourselves in Delphi/Pascal). I then realized that this seemed wasteful. I thought then that I’d use an if statement and assign a different value to a variable and then have the formula only once. But what to call the variable? “Temp”? “Denominator”? I then tried thinking like a Python programmer and thought of if-expressions. Could you use them in a math expression? Yes, you could! My problem vanished, and I ended up with (simplified version):

      score_rank = score / (high_score if score < high_score else next_highest)
      

      No extra variables. No duplicated formula. And this snippet of the formula was described in English:

      "Divide score by the high score if it's less than the high score, otherwise use the next-highest score". Thanks to the if-expression, my code read very much like the English description!

      Readability fail? Readability improvement over something like

      If score < highScore Then
        scoreRank := score / highScore (*rest of formula...*)
      Else
        scoreRank := score / nextHighest (*rest of formula repeated....*)
      

      or

      if score 255 and string literals > 255 and…. πŸ™

  3. your pascal example looks (to me) more readable and is a lot better to debug (set a breakpoint on the line below else to debug just this case, how would you do this in an inlined if-expression?).
    I may be old fashioned, but it’s mainly as simple as that: one statement per line, separate logic and assignment.

    1.   color := if condition then clRed else clWhite;
      

      Is one statement. It’s just a statement where part of the statement is a conditional expression.

      How do you debug any expression ? If I am stopped on a statement containing an expression whose result I am unsure of, I inspect the constituent parts to deduce the result (or evaluate the expression in the Immediate Window, if at all unsure). Those same techniques apply.

      I personally would rather have the ability to use it when appropriate than have to code around the fact that it isn’t available at all.

      You obviously wouldn’t use it if debugging complexity were an issue (it isn’t always) and if debugging an example such as that provided is something that someone finds challenging then they frankly have no business being anywhere near a compiler in the first place. πŸ˜‰

      It is no different in that respect than:

        const
          COLORS: array[FALSE..TRUE] of TColor = (clWhite, clRed);
      
        color := COLORS[condition];
      

      Other than being far less verbose (and therefore less prone to silly mistakes) than the alternatives.

      The const array technique is less susceptible to such mistakes than the unnecessary duplication of statements (4 in the place of 1) that an if-then-else entails. But the const array approach isn’t quite as flexible however, being able only to select between alternative constants and involves scattering the key, constituent parts of a statement around, rather than keeping them with the statement itself.

      All in all, the pluses of if-expressions far outweigh the minuses.

      1. I do understand your “one statement” statement. However I personally prefer two lines:

        if condition then
        color := clRed
        else
        color := clWhite

        I don’t like the ternary operators in ther languages (PHP, Perl, etc.) as well, e.g.:


        $message = ($coolFactor >= 10) ? "You're one cool dude!" : "Sorry, you aren't that cool!";

        message := if coolFactor >= 10 then "You're one cool dude!" else "Sorry, you aren't that cool!";

        if coolFactor >= 10 then
        message := "You're one cool dude!"
        else
        message := "Sorry, you aren't that cool!";


        IMHO last one wins. well, that’s a personal decision I think πŸ˜‰

    2. I forgot also to say, both examples are Pascal. It’s just that one of them isn’t stuck in the 70’s. πŸ˜‰

      (and I say that as a child of that decade, who only very narrowly avoided being born into a pre-decimal Britain) πŸ™‚

    1. Exactly right. Dogmatic principle should not trump the considered application of all available tools in the pursuit of readability.

      Of course, not all languages equip the developer with as complete a set of such tools as others. πŸ˜‰

Comments are closed.