#1223 Static imports

qualidafial Mon 20 Sep 2010

Can we get support for static imports?

With them, things like:

using [java]org.mockito::Mockito

Mockito.verify(obj).doFoo(Mockito.eq(bar), Mockito.any(Baz#))

Can be shortened down to:

using [java]org.mockito::Mockito.*

verify(obj).doFoo(eq(bar), any(Baz#))

Which is a lot less noisy.

yachris Mon 20 Sep 2010

I REALLY didn't like this when it showed up in java... yeah, it makes this case prettier, and that's nice, but in general it's just confusing.

I think it pretty much requires you to use an IDE... you come across foo(bar) and there's no foo in this class, its superclasses, etc. So it's grovel through the import statements time, hoping to find it.

-1

qualidafial Tue 21 Sep 2010

@yachris: I can see your point. Where I work, our best practice is to use explicit imports over wildcards, for exactly the reasons you stated. However I still don't see this as much different than what happens when you import a whole pod:

using fwt
using gfx

class Foo
{
  Image image := ... // did this come from fwt or gfx?  time to consult docs..
}

So you already have to "grovel," as you put it, through your imports to find out where that class came from.

I would definitely be willing to compromise on the wildcard though, in exchange for keeping static imports in general. So my earlier example would look like this instead:

using [java]org.mockito::Mockito.verify
using [java]org.mockito::Mockito.mock
using [java]org.mockito::Matchers.eq
using [java]org.mockito::Matchers.any

verify(obj).doFoo(eq(bar), any(Baz#))

It depends on the situation but most of the time I would favor those extra import statements over sprinkling redundant class names all through my test code.

yachris Tue 21 Sep 2010

Okay, your explicit example makes a lot of sense. I'd vote +1 for the explicit case.

ivan Tue 21 Sep 2010

As a workaround, it seems that it is possible to create mixin like this:

mixin MockitoTest
{
  ... verify(...) { Mockito.verify }
}

and then add this mixin to test classes

brian Tue 21 Sep 2010

I am a bit suspicious of static imports. Although it is sort of targetted at another problem, I do like C# extension methods though. That has always been my thought of how Fantom ought to go.

I also personally use Ivan's method - put my static utility methods into a mixin and then inherit from that.

tonsky Mon 8 Nov 2010

With static imports one can use static methods as functions, it may reduce code size drastically in some cases. What's wrong with them? They are explicit, accessible from any context (statics, closures etc) and are much lighter than mixins — you don't have to edit class hierarchy.

katox Mon 8 Nov 2010

@brian If you make it a habit - to put "static imports" (really all useful reusable bits) into mixins - then the inheritance operator used for "mixing in" starts to show its dark side. This is _exactly_ the place where flat composition of traits shines.

Traits can have some rough spots when it comes to static type system but overall they are better than both (inheritance) mixins and extension methods.

I have an unfinished post about traits vs mixins (I wanted to sum better examples) I'll try to finish this ASAP.

-1 to static imports.

qualidafial Tue 16 Nov 2010

@brian If you make it a habit - to put "static imports" (really all useful reusable bits) into mixins - then the inheritance operator used for "mixing in" starts to show its dark side. This is _exactly_ the place where flat composition of traits shines.

I've briefly looked at the Scala docs for traits. Can you describe how these are distinct from Fantom mixins?

Traits can have some rough spots when it comes to static type system but overall they are better than both (inheritance) mixins and extension methods.

Could you give an example where traits would be preferable to mixins/extension methods?

DanielFath Tue 16 Nov 2010

I've briefly looked at the Scala docs for traits. Can you describe how these are distinct from Fantom mixins?

Mixin and traits aren't that different, however there are few crucial differences.

From Wikipedia

Traits are similar to mixins, but whereas mixins can be composed only using the inheritance operation, traits offer a much wider selection of operations, including symmetric sum, method exclusion, and aliasing. A Trait differs from an abstract type in that it provides implementations of its methods, not just type signatures.

Another nice thing Scala does for the traits is the dynamic behavior and their stacability.

class Ball {
  def properties(): List[String] = List()
  override def toString() = "It's a" +
    properties.mkString(" ", ", ", " ") +
    "ball"
}

trait Red extends Ball {
  override def properties() = super.properties ::: List("red")
}

trait Shiny extends Ball {
  override def properties() = super.properties ::: List("shiny")
}

object Balls {
  def main(args: Array[String]) {
    val myBall = new Ball with Shiny with Red
    println(myBall) // It's a shiny, red ball
  }
}

katox Tue 16 Nov 2010

There are slightly different definitions (at least for mixins) floating on the web so I'll rather write the post more detailed.

Mixins are somewhat in the middle between multiple inheritance and single inheritance with interfaces - mixins are components to be reused (they contain code) but they use _inheritance_ to propagate methods further in the hierarchy (so they must have restricted access to object state). The usage of inheritance sometimes makes it difficult to use a mixin "as is" and some kind of glue code might be necessary to fit everything together.

Traits are simple groups of methods which serve as units of code reuse. Unlike mixins traits can be used to simply _compose_ another trait or class. The trait composition is _flat_ - the hierarchy is only a structuring tool and it has no effect on the meaning of the resulting class. A trait :

  • provides a set of methods with implementations (can be implemented only partially),
  • requires a set of methods to parametrize the provided behavior,
  • must not specify state or directly reference it,
  • can be composed; conflicting methods are excluded from the composition,
  • can be nested but the result is equivalent to flattened traits.

A class can be constructed from a group of traits plus state variables and methods needed for parametrization (glue). The semantics of methods is independent of whether they are defined in a trait or in a class that uses the trait.

The trait composition doesn't affect single inheritance which is an orthogonal concept. Specifically, the hierarchy navigation works the same as in the traditional single inheritance. For instance, for super keyword the referred method is to be found in the superclass of the class that uses the trait - i.e. it is the same as if the method using super was defined inline, in the class definition itself.

Note that in Fatom you current can't use super in mixins (impl. limitation) but if possible such super would reference mixin parent (as in multiple inheritance) not the host class parent.

katox Tue 16 Nov 2010

What Fantom gets right with mixins is that it doesn't support automatic overriding in multiple inheritance hierarchy (unlike Smalltalk or Python) which can lead to silent bugs (overriding by accident). Ruby mixins suffer from the same problem even though the mechanism itself is different.

Fantom currently reports a compilation error if there is such a situation. This is more limited but leads to much simpler method resolution (quite unusual behaviour - I could find only Eifell language which the same semantics). The downside is that you have to specify method resolution explicitly (but you can't use named supers in mixins).

katox Tue 16 Nov 2010

Could you give an example where traits would be preferable to mixins/extension methods?

I'd say traits are preferable to mixins because devs need to resolve collisions explicitly, usually by aliasing methods, excluding methods, and merging a new trait that uses the aliases. Fantom mixin conflict resolution possibilities are more limited. I also consider the possibility of using super a plus in shared trait code.

Note: traits in statically typed language seem to need generics to be easily composable, however Fantom This type should be sufficient.

I could not think of a simple example but the difference can be seen in real-life apps which tend to grow to hideous multi-parent hierarchies -- see a plone (python UI mixin based framework) app diagram.

Adding features using traits is preferable to extensions methods - upfront (or with availability of source code). It is however impossible to extend 3rd party code with static traits. The magic of seemingly calling code on objects which didn't implement those methods is just not there. But restrictions apply even to extension methods in such cases.

katox Tue 16 Nov 2010

The last addition to the monologue above: Scala allows adding traits to an object on its instantiation (among other trait enhancements). This is quite safe because no inheritance operator is needed. But it is still not powerful enough to enhance 3rd party code with no sources after the fact (they use implicit conversions for that).

I've stumbled upon a nice write-up with code samples on this - Scala Traits vs. C# Extension Methods paragraph (seek to the last section).

Login or Signup to reply.