1

I want to accomplish something a little different from standard mixins. I want to construct a trait whose new fields are computed based on the fields of the class (or trait) it extends.

For instance, if I had a class like this:

class Point {
    var x: Int = 0
    var y: Int = 0
}

and I wanted to produce a class like this:

class Point' {
    var x: Int = 0
    var y: Int = 0
    var myx: Int = 0
    var myy: Int = 0
}

I'd like to be able to write a function that computes the field names myx and myy and then mixes them into the class using a trait. Here's some made up psuedo-Scala for what I want to do:

def addMy(cls: Class) {
    newFields = cls.fields.map( f => createField("my" + f.name, f.type) )
    myTrait = createTrait(newFields)
    extendClass(cls, myTrait)
}
Jonah Kagan
  • 447
  • 1
  • 4
  • 7
  • Why would you want to do this? The only way to access those elements would be via reflection, which seems more work than the more obvious alternatives (e.g., some sort of name/value map), no? – cheeken Feb 20 '13 at 02:17
  • With macros all things are possible. ... I think... I've not gotten into them yet. – Randall Schulz Feb 20 '13 at 02:49
  • 1
    unfortunately it is not yet possible to create classes (or traits) via macros that are usable outside the macro - AFAK. At least with Scala 2.10. The other way - creating an object that implements a (pure) trait would be possible. So anyway - could you give us an example, why that behavior would be useful? – michael_s Feb 20 '13 at 05:14
  • @cheeken, why would I only be able to access the added elements via reflection? Could I not just create an instance of the new class and access `instance.myx`? – Jonah Kagan Feb 21 '13 at 03:08
  • @michael_s, this type of pattern would be useful, say, if I were writing an ORM. I could allow the user to create a class (representing some data model) with a relation to another model. Then I could add fields to the class to represent the fields of the related model. – Jonah Kagan Feb 21 '13 at 03:09
  • So if I had a `Person` with a relation to a model `Car` (with an `id` field), I could add to the `Person` class the field `car_id`. Django does something like this I believe (http://stackoverflow.com/questions/2846029/django-set-foreign-key-using-integer). – Jonah Kagan Feb 21 '13 at 03:19
  • OK understood - so, I'd probably go with the answer below (using Dynamic). It is not typesafe however and it's more or less like using a HashMap. The publishing of created classes and traits in a package - looks like they are adding it to the upcoming Scala 2.11. The next Milestone is due in a few weeks. See [here](http://docs.scala-lang.org/overviews/macros/typemacros.html) for further information. – michael_s Feb 21 '13 at 05:35
  • @JonahKagan If you are dynamically constructing a trait, you are doing it at run-time (not compile time), and so your source code cannot reference the dynamically-created elements. It sounds like you are looking for a static code tool, though, so my earlier comment is not applicable. You may want to remove the word "dynamically" though. – cheeken Feb 21 '13 at 16:03

1 Answers1

0

The easiest way I can think of to achieve such a behavior would be to use Scala 2.10's Dynamic feature. Thus, Point must extend Dynamic and you can "mix in" arbitrary fields by adding the calculation logic to the member functions selectDynamic and updateDynamic. This is not quite what "mix in" actually refers to, but it nevertheless allows you to add functionality dynamically.

To ensure that it is possible to check whether a specific functionality has been mixed in, you can for instance use the following convention. In order to check whether a field is mixed in, a user can call hasMyx or has... in general. By default your implementation returns false. Each time you mix in a new field the corresponding has... function would return true.

Please note that the return type of all your mixed in fields will be Any (unless all your fields are of the same type, see for instance this question). I currently see no way to avoid a lot of casting with this design.

Community
  • 1
  • 1
bluenote10
  • 23,414
  • 14
  • 122
  • 178