#1270 Defensive copying on generated getter and setter

Henri Wed 27 Oct 2010

Just an idea for discussion purposes:

By default the generated getter and setter do not perform defensive copying (see Effective Java). I would propose the following syntax for fields:

Person person  //No defensive copying
Person (anotherPerson) // Use defensive copying

Motivation: the parens give a visible impression of some sort of protection of the field.

The generated getters and setters should use a copy constructor (the copy constructor should be called makeCopy(Person person) or something similar):

Person person
{
  get { return Person.makeCopy(&person) }
  set { &person = Person.makeCopy(it) }
}

This is of course only useful for mutable fields, so the following should generate a compiler error:

const Person (person)

yachris Wed 27 Oct 2010

I think I'd prefer to see, if possible, a facet such as @DefensiveCopied rather than special syntax.

qualidafial Wed 27 Oct 2010

Going a step further, I'd love to have a way to mark certain annotations as "magic," like an implicit call to a compiler plugin whenever a magic annotation is used. e.g.

@Magic { plugin = BindableCompilerPlguin# }
facet class Bindable
{
  const Obj? eventType
}

Then I could write a compiler plugin that turned this:

@Bindable Str firstName

into this:

@Bindable Str firstName
{
  set
  {
    &firstName = it
    listeners.fire(Event(#firstName))
  }
}

brian Thu 28 Oct 2010

Going a step further, I'd love to have a way to mark certain annotations as "magic," like an implicit call to a compiler plugin whenever a magic annotation

Agreed - this is where we need to go

andy Thu 28 Oct 2010

+1 qualidafial

Henri Thu 28 Oct 2010

I believe the general rule should be that facets do not alter the semantics of the annotated element. That is why I didn't propose a facet in the first place.

However, a facet called @Magic might be an exception to the rule.

yachris Thu 28 Oct 2010

Well, annotations in Java are metadata-only, but that doesn't mean they have to be that way in Fantom. And (having a hard time believing you were serious, but just in case...) having a facet called @Magic which is the only way to do non-metadata behavior is probably too broad.

Henri Thu 28 Oct 2010

I agree that facets do not have to be like annotations in Java. I just think that magic behavior is very dangerous for the readability of the code. The magic part, if any, should be only what the (knowledgeable) reader could reasonably expect or logically assume, but nothing more, and should certainly not have suprises. Facets would become some sort of template facility, which I don't think is a good idea in general.

The best way to tell the reader that something magic, as an exception, is going on is to explicitly mention that. Of course, @Magic should not be the only mechanism to introduce non-metadata behavior, but I think some kind of visible distinction would be beneficial. How about @@Bindable?

katox Thu 28 Oct 2010

Well, annotations in Java are metadata-only,

It depends on your point of view. Some annotations (ORM typically) just cause runtime bytecode generation so wild that the resulting bytecode couldn't be expressed directly in java syntax. There are also IDE annotations like IDEA's @Nullable and @NotNull which I wouldn't count as pure metadata.

+1 - it would make sense to have a special name or syntax for facets which cause additional compiler calls. I agree with yachris that a single name @Magic is not enough.

qualidafial Thu 28 Oct 2010

Well, annotations in Java are metadata-only, but that doesn't mean they have to be that way in Fantom. And (having a hard time believing you were serious, but just in case...) having a facet called @Magic which is the only way to do non-metadata behavior is probably too broad.

Just to clarify, the idea with the @Magic annotation is so that you can create your own facets with magic behavior. Ergo my previous example:

@Magic { plugin = BindablePlugin# }
facet class Bindable
{
  Obj? eventType
}

By annotating the @Bindable facet as @Magic, we are saying that using the @Bindable tag will enhance your code in some way. For example the following class:

class Foo
{
  @Bindable Str foo
}

Would be transformed by the BindablePlugin compiler plugin into something like this:

class Foo : EventDispatcher
{
  override once Listeners listeners() { Listeners() }

  @Bindable Str foo
  {
    set
    {
      &foo = it
      listeners.fire(Event(Foo#foo))
    }
  }
}

qualidafial Thu 28 Oct 2010

Another idea is to use facet-based compiler plugins for things like limiting what targets a facet can be applied to, similar to the @Target annotation in Java:

@Magic { plugin = TargetPlugin# }
@Target { types = [ ElementType.facet ] }
facet class Target
{
  const ElementType[] types := [,]
}

enum class ElementType
{
  type, facet, field, constructor, method, argument, variable
}

qualidafial Thu 28 Oct 2010

The best way to tell the reader that something magic, as an exception, is going on is to explicitly mention that. Of course, @Magic should not be the only mechanism to introduce non-metadata behavior, but I think some kind of visible distinction would be beneficial. How about @@Bindable?

I don't care for @@Bindable syntax, but I do think there would be value in requiring a different syntax so that someone reading the code would see right away that there is compiler magic going on.

Bonus points if the syntax looks like an emoticon for magic without being tedious to type. :)

yachris Thu 28 Oct 2010

I stand corrected on several fronts today :-) ... I didn't know that java annotations could be used for behavior, and now I understand the intent of the @Magic facet.

However, the original suggestion:

like an implicit call to a compiler plugin whenever a magic annotation is used...

is slightly scary, for the following reasons. I use Eclipse, which has a plugin architecture, which is both fantastic and kind of a nightmare. The nightmare part is our old friend, dll hell... you can wind up wanting two completely separate plugins, but you either can't use both or you can't use them simultaneously (or you have to go back several versions on one or both) because they depend on some third plugin. Companies have made whole businesses out of configuring Eclipse!

My concern is that I wouldn't be able to compile someone's source code I wanted to use, or I would have to somehow maintain "N" versions of the Fantom compiler plugin directory.

I certainly vote +1 on the idea, but it bears serious thought on the implementation.

DanielFath Thu 28 Oct 2010

+1 to @@Magic. IMO every other type of facet should try to be as dumb as possible.

msl Thu 28 Oct 2010

Personally, this strikes as a horrible opening for massive abuse and confusion.

I agree with the take that annotations/facets should be metadata only - while ORM does generate layers upon layers on top of my code, it doesn't change the internal meaning of my code - which the @Magic idea does.

I'd rather look at more adventurous solutions at the language level than shoe horning facets into something larger than (IMHO) they should be.

The only solid use case I've seen so far for @Magic is the Bindable case - which could easily be achieved by allowing the specification of mix ins on an arbitrary object at declaration time as you can do with scala (although the syntax with fantoms Type name rather than scalas name: Type is a little ordinary):

class Foo
{
  Str : Bindable foo    
} 

(later)

I should also say that I'd feel less strongly about it if we weren't talking about compiler tricks. I'm not sure how you could write the hook to @Bindable without using the AST - but having it as a single piece of source code I can browse through and debug as opposed to internal AST hackery would make it at least bearable.

brian Thu 28 Oct 2010

I won't go into all the details, but this issue has been heavily discussed on the forum already:

  • there is general agreement (I believe) that things like creating Bindable fields should not require boiler plate
  • this might start to sound like "macros", but in general I am opposed to them if it hurts the readability of the code
  • we have discussed this in terms of DSLs maybe borrowing the <|...|> syntax
  • we also had a very promising discussion about how you might be able to mixin types into the fields/methods themselves; to me this is actually maybe the closest to what I would consider the most elegant solution

But there is no concrete design in-place yet, and I want to finish what we have as a 1.0 release before tackling something this huge.

tactics Fri 29 Oct 2010

this might start to sound like "macros", but in general I am opposed to them if it hurts the readability of the code

The problem with macros arise when the macro solution is lightweight. This encourages application-level designers to try to improve the language.

As long as any macro system is heavyweight and requires a deeper understanding of the compiler, they should be less of a problem. Instead of ad hoc solutions written per-application, you'd end up with a standard library of well-known compiler plugins.

As long as there is a feeling of standardization to language extensions, it doesn't matter if they are baked into the language or available from a third party.

Login or Signup to reply.