freemarker-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Daniel Dekany <ddek...@freemail.hu>
Subject Re: [FM3] improve “null” handling
Date Sun, 05 Mar 2017 08:38:38 GMT
Saturday, March 4, 2017, 11:26:04 PM, Daniel Dekany wrote:

> Saturday, March 4, 2017, 7:19:09 PM, Pedro M. Zamboni wrote:
[snip]
>> But seriously now, I’ve written this part of the message in a
>> different day than the rest (to be honest, I’ve written this message
>> over the span of a couple days, but that’s unimportant), and I’ve
>> recently had an idea: what if `:` was its own operator?
>>
>> In regular user terms, it’d explode on bad nulls, but it’d accept good
>> nulls and return the right‐hand‐side in that case.
[snip]
>> ${maybeNull!.foo.bar:"xxx"}
>> ```
>>
>> Now, whenever `maybeNull` is null, `"xxx"` will be shown. But
>> whenever, for example, `.bar` is null, it will throw.
>
> That's a good idea, and I especially like that now we don't suppress
> the null problem at `.bar`. I wish we had `?` instead of `!`, and then
> we not only managed to "generalize" `?.`, but also the `?:` operator.
> (Such a same that some 14 years ago the role of `?` and `!` was
> selected the other way around...)
>
> But... it has some problems, because of which I think we better resist
> the temptation. At least in the primary (<#>-ish) language. I was here
> earlier BTW (not in this thread, but months ago and alone). Not sure
> the syntax/semantic was the same, but I wanted to prevent suppressing
> the null at `.bar`, and that resulted in an extra symbol to be used
> after the `!` (just like here, the `:`), and the resulting problems
> were the same:
>
> - I wanted to use `:` for namespace prefix separator... now it's
>   taken. What to do? I can use something like `|`, but it's less ideal
>   maybe...

Actually, if step back a little we ignore FreeMarker tradition,
there's another combination... (Though ignoring tradition is
politically problematic, as I have already noted in this thread.)

The main reason we want `:` for namespace prefix separator is so that
you can write `foo?my:f`, where `:` hash higher precedence (is more
sticky) than `?` or `.`. As `?` and `.` has the same precedence,
`foo?my.f` wouldn't work because it means `(foo?my).f`. But there's
another way around that; decreasing the precedence of `?`. Then it
stats to behave more like the pipe operator you can be familiar from
some other languages. So let's use `|` instead of `?`, and now instead
of `x?upperCase?trim`, you write `x|upperCase|trim`. Now
`x|upperCase|trim`. And now `x|my.f` (or `x|my.f|trim|my.2`) just
naturally works. We have still given up two things with this though,
but if we ignore tradition, this is maybe still the best compromise
you can have:

- We won't have separate namespace for namespace prefixes (or we will
  have some strange syntax for it... but let's say we won't have for
  now). That is, if you have `#import '/my.ftl' as my`, then the `my`
  variable it produces might shadows `my` in the data-model, and a
  local `my` variable might shadows the namespace prefix (if the
  parser allows such bad practice at all). This is can become a
  maintainability/readability a problem with auto-imports (because,
  you can break templates by adding auto-import to the Configuration,
  etc.). But it might as well manageable in practice. It's pretty much
  like when you make up some global rules regarding your data-model,
  like, that in your framework it always contains the "spring"
  variable (as it is FM2 Spring integration). Actually if we don't
  need `:` for namespace prefixes that badly anymore (because
  `foo|my.bar` have solved the "call a custom function with postfix
  syntax" problem), we mights as well better of with the the simpler
  looking "just use `.` everywhere" approach (which FM2 follows too).
  That has a quite substantial advantage to balance out its known
  problems: Sometimes its not obvious if a group of methods/variables
  should be just part of an object in the data-model, in which case
  you write `my.f` in templates, or it should be orthogonal to the
  data-model and solved with an auto-import, in which case you would
  write `my:f` if we don't hijack `:` for null handling. But, you might
  not want the template authors to be affected by such fine details
  (whether you decided the put your "library" into the data-model or
  you have opted for using auto-import feature instead). Most of them
  won't even understand the difference, and it just comes through as
  an inconsistently (that sometimes you have to write `stuff.aMember`,
  and some othertimes `stuff:aMember`).

- In FTL2 you can do this: `x?foo.bar`, which means `(x?foo).bar`.
  With the pipe syntax you had to write `(x|foo).bar`. It's certainly
  not a that big deal, as you hardly ever access the members of the
  result of a built-in. Except, there's `?api`, where you do exactly
  that. Like `myMap?api.myAppSpecMethod()`. Well, we can introduce
  `|.` if that's often a problem, like `myMap|api|.myAppSpecMethod()`.
  Or, we can attack the problem of `?api` outside built-ins, and say
  that when you write `foo.bar`, there are possibly multiple
  namespaces *inside* `foo` where "bar" can be searched. (In Java for
  example, there's a filed namespace and a method namespace - you can
  have both a field and method called "x", and they won't clash, and
  the syntax of the member access tells Java which namespace should be
  searched.) And so we can come up with a syntax for that, like
  `foo.bar@inThisNamespace`, and so in the last example we had
  `myMap.myAppSpecMethod@api()`. It's a bit like e-mail addresses.

Better yet, with this you now have freed up `?` for what it should
really mean; a marker on something that is optional (something that
produces "good null"-s when it produces null-s). So we can replace `!`
with `?`. And so you can write `?.` and `?:`, which is more familiar
(like from Groovy, and almost from Java too - see Project Coin), and
`varName?` (as in `${foo?}`) can be familiar too from Ceylon, Kotling,
C#, etc. That we don't have an alien looking syntax is a big
"political" advantage. There was always some hate generated towards FM
because of that. By the time the user realizes (if he does) that those
aren't really the familiar operators, but are just some common
applications of the FM-specific `?` and `:` operators, it's too late,
they already have written some templates... <evil-grin/>.

So the big quesiton is, if I haven't missed something above, and guys
around here like the new syntax too, do we dare to introduce a so
striking change and still call the result FreeMarker? Well, I'm brave
and all, but I think then the honorable way is calling the result
FreeMarker NG 1.x.x, rather than FreeMarker 3.x.x. It's based on the
FM2 code base, keeps the core FM2 principles, but has a too different
look-and-fell.

> - It's not how you did it in FM2 (breaks tradition... possible but hurts)
> - It's one more symbol for specifying a default, which meant to be
>   a basic templating operation.

(Here I meant `x!default` VS `x!:default`. Although if you write it as
`x?default` and `x?:default`, it's more familiar, at least if someone
has used Groovy, and so is less acceptable psychologically.)

> - It's yet again something that's kind of difficult to grasp for the
>   average user. I mean, users will keep writing ${x:'-'}, which
>   *never* works. So we can catch it during parsing and tell in the
>   error message why it's wrong, but still.

>> -----
>>
>> By the way, sorry for not responding sooner.
>>
>

-- 
Thanks,
 Daniel Dekany


Mime
View raw message