Lazy parameters, $IF, compound assignments

Here is a highlight of recent changes on the SVN side of DWScript:

  • lazy parameters have been introduced.
  • $IF has been added to the conditional directives, alongside the Declared() and Defined() special functions.
  • Compound assignment operators +=, -=, *= and /= are now supported.
  • Preparations for operator overloading.

lazy parameters

Discussed recently, they’re an extra parameter passing call convention, alongside of var and const. A lazy parameter passes neither the value nor the reference, but the expression. That expression is lazily evaluated when the parameter is used in the procedure, and evaluated as often as it is used.

This allows to write conditional functions like the ternary operator (like Prism’s iif) without involving compiler magic, logging functions where the log expression to isn’t evaluated unless the log is active like discussed there, or all kind of purposes like that of lightweight anonymous functions, f.i.

procedure PrintNTimes(n : Integer; lazy s: String);
begin
   while n>0 do begin
      Print(s);
      Dec(n);
   end;
end;

var i : Integer = 0;
PrintNTimes(5, IntToStr(Inc(i))); // will print 12345

Implementation is still experimental, and units tests still limited, so more testing and issues reports are welcome 😉
Note that lazy parameters are currently available in the language, are implicit in internal “magic” functions, but aren’t available yet for functions declared via a TdwsUnit.

$IF, Declared() and Defined()

These functions have been introduced for conditional directives, but are also accessible in the code:

  • in conditionals, they work on what is defined or declared during the compilation when the conditional is reached.
  • in the code, they work on what is defined or declared at the end of the compilation.

$IF expressions have access to all constants defined up to the point where the $IF is located, as well as all stateless/invariant functions and operators operating on constants (f.i. you wished to, you could do hyperbolic sinus computation in a $IF, provided the parameter is from a constant expression).

Compound assignment operators

The +=, -=, *= and /= operators are now supported, but their use is at the moment only allowed on variables.
You can not use them on functions, properties, classes, or any other case where they could be made ambiguous by future language evolutions.

The limitation is introduced as they are planned to become first-class operators (as in C++), rather than just syntactical sugar or typing convenience like in C# or Freepascal. They’re intended to allow mutability.

6 thoughts on “Lazy parameters, $IF, compound assignments

  1. I have to say I’m disappointed in these things – it goes against the nature of Pascal imho.

    Given:

    Fn(literal, variable, fn());

    The spirit of Pascal is to keep things simple – I should be able to visualise what will happen when this statement is executed.

    In this case I expect fn() to be evaluated before being passed to Fn().

    I cannot tell simply from reading the statement, what will occur when the statement is executed. I now have to inspect the parameter list in the Fn() declaration in order to deduce what the statement will do.

    Pascal avoids this by providing for functions as first class entities. If Fn() accepts a function() then it can declare itself as such, but – and this is the crucial part – I cannot mistakenly express an invocation of Fn() using a CALL to fn(), given:

    Decl: Fn(type, type, fnref type)

    Fn(literal, variable, fn()); // will fail to compile
    Fn(literal, variable, fn); // OK – evaluated later

    And conversely, given:

    Decl: Fn(type, type, type)

    Fn(literal, variable, fn()); // OK – evaluated now
    Fn(literal, variable, fn); // will fail to compile

    But now, given:

    Decl: Fn(type, type, lazy type)

    Fn(literal, variable, fn()); // OK – but will be evaluated later
    Fn(literal, variable, fn); // will FAIL to compile

    In other words, it creates a third passing mode for function calls, a mode which cannot be determined by *reading* the code.

    Imagine trying to read a book where the author used an existing tense in a new way that was not distinct in the grammar from regular usage of that tense, and which required consultation with appendices for EACH usage.

    Reading such a book would be intolerable.

    Operator overloading is similarly corrupt.

    I know this is scripting, not Delphi core language, but that imho is a reason to keep such things OUT. The types of user likely to get involved with writing script (typically provided for end-users to extend products) are actually LESS likely to get their heads around such things. Not more.

    Just imho.

    What would be useful is someway at least to turn these features off in the DWS engine.

    Any plans for such a thing?

  2. The parameter passing ambiguity is already in the language with the var qualifier: you don’t know if you’re passing a reference or value unless you look at the declaration. The evaluation ambiguity is already in the language too, f.i. with boolean shortcut evaluation. It’s more a matter of making those non-compiler-magic.
    I agree with the ambiguity of parameter passing, and tbh, I’ve been considering adding some explicitness requirement (in the form of a qualifier at the call site, similar to some other language’s “byref”, which could be a repetition of “var” or “lazy” in Pascal), though that would be straying from the pascal canon, I’ll probably introduce it at some point.
    Making all those features switchable is trivial enough (because the compiler is simple, and will be kept as simple as possible).
    Operator overloading isn’t currently planned for surfacing in the language (apart from class compound operators), but is a necessary requirement for the engine, to allow introducing new base types more easily (timestamp, vectors, etc.) and moving some base types out of the core (Variant). Also, DWScript being a script, I don’t think it would be wise to encourage people to write new base types in DWScript, because the performance wouldn’t be there. The strength of a script is in flexibility, security and resilience to crashes IMHO, performance and complexity is more the realm of “real” languages, so on that front I want it to stand on the shoulders of the host dev environment (plus, the problems don’t address the same category of developers, so the distinction makes a lot of sense imho).
    I’ll probably post a “roadmap redux” to replace all the above in perspective, ad interim, I encourage you to re-read the old roadmap I posted a few months ago 😉

  3. I would rather go for defining the method’s body in the interface, i.e.:
    type
    THowCool = (hcNotCool, hcInteresting, hcExcellent);

    TMyClass = class
    private
    FHowCool: THowCool;
    public
    function HowCoolIsThis: THowCool;
    // we can ignore the “begin” keyword or make it optional
    Result := FHowCool;
    end;
    constructor Create;
    FHowCool := hcInteresting;
    end;
    end;

    I’m expecting people to swear me because it’s more C-like than pascal, however don’t forget that DWScript is basically a script so it can have nice(IMHO) features like this.

  4. @Dorin Duminica
    This would require multi-pass parsing, if you want to use in your implementation methods that haven’t been defined yet f.i., but the DWScript parser is single-pass.
    Also I don’t think you could ignore the “begin” without encountering syntaxically-ambiguous cases.

  5. Re: “var” being ambiguous.

    I don’t disagree that it’s similar, however, the possibility for confusion is far less. “var” does not change the evaluation order of parameters, it merely allows a function to modify a passed value (in that respect it is LESS ambiguous because it is impossible to pass an expression to a var parameter).

    It is also less ambiguous because unlike the case of a function that accepts a simple value which may or may not itself be the result of an expression, someone calling a function that accepts a var param typically will know that that is exactly what they are doing, because they are expecting to get some sort of result via that var parameter.

    But I was happy to read your reply in general – you do seem to have at least one eye on the most likely target audience for DWScript. 🙂

    And I still find lots to be interested and excited about DWScript overall and hope to reach a point soon where I can start spending some time with it myself and possibly contribute, instead of hawking from sidelines.

    🙂

Comments are closed.