1

Consider such code (this is just example not real code):

class Foo(url : String) extends Bar(url)
{
  def say() { println(url) }
}

It compiles and it works. With nonsense results "of course". I am too newbie to judge, but for me it serves no purpose but confusion -- by definition it is impossible that the argument of the constructor could be reachable directly in another method.

Could someone more experience in Scala could point out condition when it might work or make sense? Or confirm my suspicion this is flaw in current Scala compiler.

Update

class Bar(val Url : String)
{
}

Why "nonsense". Because my will (no "var" and no "val" in Foo) was to just pass the argument, nothing else. So when I actually use the constructor argument it is just matter of what entity I use by mistake. When you write on purpose, you hit the jackpot each time, but if you don't (i.e. you make a mistake in spelling), you use entities by random. It is not the code does not make sense, it is the computation just does not make sense, I could roll a dice as well. There is a method scope, and I don't see a reason why there shouldn't be constructor scope.

Update -- workaround

So it seems this evil construct is really part of the language (by design). As UserUnknown and Tomasz Nurkiewicz suggested the only line of defense against making stupid typo is convention, however lower-upper case is not good. "A" and "a" differ a lot, but "U" and "u" not much. Introducing one special character (as Tomasz showed) is much better, because it is possible to visually detect fishy usage of constructor argument.

I will use "$" for "just-passing" constructor arguments, it is harder to type for me, and you don't see this character too often in the code.

Thank you for the answers!

Why it is evil? Because implicit actions should be allowed explicitly by users -- good examples are "dynamic" in C#, implicit conversions in Scala. And examples of breaking this rule which led to tons of problems are -- implicit conversions to bool in C++, implicit constructors in C++, declaration by usage in Perl. And this particular case is very, very close to the mentioned perlism, in Perl finally there was change in interpreter to detect such misusages, why Scala repeated the same mistake? I wonder.

greenoldman
  • 16,895
  • 26
  • 119
  • 185
  • It's not nonsense. Why do you think it's nonsense? – dave4420 Dec 04 '11 at 21:49
  • Because I meant "Url" from Bar (base class), but I made typo and I get nonsense result (null, instead of string with content). This is very close to "declaration by usage" and I "hate" such thing on the same ground as I "hate" dynamic languages ("hate" here has technical, not emotional basis). The only defence is now testing even simplest construct despite the fact it could be detected on compilation stage. – greenoldman Dec 04 '11 at 21:56
  • Then what would the point of having constructor arguments be? – Dan Burton Dec 04 '11 at 22:02
  • @macias -- I've been staring at your code and it looks like it *should* do exactly what it *does* do. Admittedly, “should” is largely in the eye of the beholder, but there's one data point for you. – Michael Lorton Dec 04 '11 at 22:50
  • Is `Url` an attribute or an method in the base class? Why don't you stick to the convention, and name these with lowercase? – user unknown Dec 05 '11 at 01:37
  • @user unknown, do you really believe I have class Foo and Bar? This is just example to show the point -- with IntelliSense it is even easier to bring argument construct and create a field on the fly, because at some point you hit the "wrong" key. – greenoldman Dec 05 '11 at 06:47
  • @Dan Burton, to construct inner objects or to pass them to base class. However it is impossible to use them directly in any method, in such case you should create a field (add "val"). – greenoldman Dec 05 '11 at 07:08
  • I do wish we could start this question from scratch, with some idea of what `Bar` looks like, what the offending behaviour actually is, and a less pugnacious attitude. Words like `nonsense`, `evil`, `stupid` and `Perl` do not get the issue off on the right foot. – Duncan McGregor Dec 05 '11 at 08:42
  • @User unknown's first question is extremely pertinent, and you haven't answered it! If you want help, or to make the world a better place, work with us. If you just want to vent about language design, write a blog post. – Duncan McGregor Dec 05 '11 at 08:52
  • @Duncan McGregor, it is val, see the update. – greenoldman Dec 05 '11 at 08:58
  • In which case, what do you expect the result to be, because I see nothing confusing - `new Foo("urlvalue").say` prints `urlvalue` in all circumstances? – Duncan McGregor Dec 05 '11 at 09:17
  • @Duncan McGregor, I was just illustrating the lack of constructor scope, not my real code. My point is, there is Url (just an example), and it should be used, not "url", because it is just a pass-by argument. I updated my post a bit more to emphasis this -- sorry for not making this clear from the beginning. – greenoldman Dec 05 '11 at 09:36
  • I agree that lack of constructor scope can sometimes be an issue - in particular local vals. But in the case of your question, you've not actually created a new field at all, as the compiler will use URL from Bar - see my answer. – Duncan McGregor Dec 05 '11 at 10:02
  • @Duncan McGregor, you are right, and thank you for the answer, but here the point was using constructor argument outside, it was against my **conscious** intention, of course in realms of Foobar it was the same, but real world is more complex. – greenoldman Dec 05 '11 at 10:21
  • 1
    It is not the fact, that a lower u looks similar to an upper U. It is about the fact, that you stick to naming conventions, and never use an upper U without very good reason. And if you don't use sometimes lower u, and sometimes upper U, you will not step into that trap. – user unknown Dec 05 '11 at 15:56
  • @user unknown, so you are saying, that there is naming convention, and if I followed it, compiler would recognize the argument leak, and limit the scope of arguments to constructor only (instead of entire class)? Is this correct? If not, you are completely missing the point, it is about the scope, not the letter case. – greenoldman Dec 05 '11 at 21:07
  • @macias: Well, no, I see here 2 things unfortunately intermixed. We have a named parameter for the ctor, which is accessible, after the ctor is run. This is different from Javaland, and a nice discussion topic. But you pollute it by a made up example, where different errors (you don't know what the class does, you don't know what the base does, and you write the paramter in the wrong way without realizing it) play together to make a problem. A similar, but different problem in Java is, when local variables hide attributes. – user unknown Dec 06 '11 at 00:42
  • @user uknown, "We have a named parameter for the ctor, which is accessible, after the ctor is run" -- and that's what I asked for. "But you pollute it by a made up example" -- example was not best, I admit, because it allowed to focus on naming, instead focusing solely on using argument OUT of scope of constructor. And no naming convention is processed by compiler here (the only exception I know is matching pattern, when casing switches compilation). – greenoldman Dec 06 '11 at 21:00

3 Answers3

12

Your suspicions are entirely merit-less. This is by design.

Parameters of a class are part of the class. They'll be preserved as field if necessary (such as in your example), or not if they are never used outside construction.

So, basically, if you don't need it as a field, it won't be. If you do, it will. And you'll never write a single extra character of code to tell the compiler what it can figure out by itself.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • Well, in the example as above it leads to non-sense results, so it is something like "if this can lead to buggy program, compiler automatically allow it". IOW -- I would like to forbid such automatic fields, because obviously I am just calling base class constructor + I made a typo. – greenoldman Dec 04 '11 at 21:47
7

It's not a bug, it's a feature. In fact, a really nice one. Need an example how useful it is? Here is how I use it with Spring and dependency injection via constructor:

@Service
class Foo @Autowired() (bar: Bar, jdbcOperations: JdbcOperations) {

    def serverTime() = bar.format(jdbcOperations.queryForObject("SELECT now()", classOf[Date]))

}

Equivalent code in Java:

@Service
public class Foo
{
    private final Bar bar;
    private final JdbcOperations jdbcOperations;

    @Autowired
    public Foo(Bar bar, JdbcOperations jdbcOperations)
    {
        this.bar = bar;
        this.jdbcOperations = jdbcOperations;
    }

    public String serverTime()
    {
        return this.bar.format(this.jdbcOperations.queryForObject("SELECT now()", Date.class));
    }

}

Still not convinced?

Short tutorial:

class Foo(var x: Int, val y: Int, z: Int) {
  println(z)
  //def zz = z
}

x will become a variable with getters and setter. y will become an immutable variable and z will become an immutable variable only if zz method is uncommented. Otherwise it will remain a constructor argument. Neat!

UPDATE: I see your point now! The following code works as expected by accessing url variable in base class:

class Bar(val url)

class Foo(_url : String) extends Bar(_url)
{
  def say() { println(url) }
}

I agree, this is both ugly and is asking for trouble. In fact I once hit this problem myself when using Scala classes as Hibernate entities - I used constructor parameter instead of field in base class which caused duplicated field to be created: one in base class and one in derived class. I wouldn't even notice but Hibernate was screaming at runtime that duplicated column mapping was defined.

So I have to somewhat agree with you - this is somehow confusing and might be error-prone. This is the price you pay for "implicitness" and concise code.

However note that no modified and val modifier before constructor argument are different. Without modified immutable field is created, while val additionally adds getter.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • No, still not convinced (marking this as THE answer though). Your first example is completely different, you DO NOT pass the arguments for base class. In second example please introduce variable zx -- and judge, is "z" in "zz" is intentional or was is just a typo. This "feature" just begs for typos indeed -- since compiler always allow them, and you have no chance to forbid them. IOW -- how can you defend yourself against typos if compiler plays against you? Not convinced -- if you would like to have "z" accessible in entire class why didn't you mark it with "val"? – greenoldman Dec 04 '11 at 21:53
  • 1
    See my update, I finally realized what is the source of your confusion. – Tomasz Nurkiewicz Dec 04 '11 at 22:19
  • 2
    @macias The ability to shadow inherited fields accidentally is certainly cause for concern, but it's a more general problem (or useful feature, depending on your point of view), because in Scala, you can shadow as much as you want. Try, for example, this snippet: `val x = 1; {val x = 3; print(x)}; println(x)`. It is perfectly valid and prints `31`. – Luigi Plinge Dec 04 '11 at 23:04
  • @Luigi Plinge, you miss the point, there is no shadowing here, each entity is in one instance only. This is the case of dreadful "introduction by usage", i.e. it is enough for Scala to use the entity to create it on fly, without proper declaration. – greenoldman Dec 05 '11 at 06:50
  • @Tomasz Nurkiewicz, thank you for the update, yes, it's a problem. I am a "bit" surprised that language which get rid of "continue" because it is not pure (for me it is) introduced such evil construct right from the Perl nightmares. It breaks the principle "say what you mean". – greenoldman Dec 05 '11 at 06:52
  • @TomaszNurkiewicz Your last example can also be written as `class Foo(override val url: String) extends Bar(url) { ... }`, and will save you a small bit of memory. OTOH, maybe you meant it that way, in case Bar's url was not a val but a def. – user82928 Feb 26 '13 at 18:50
1

Scala creates a field from a constructor parameter when such an parameter is referenced by a method in the class. I'm having trouble finding fault with the way that this works.

For the simple case everything works as expected:

scala> class Bar(val url: String)
defined class Bar

scala> class Foo(url: String) extends Bar(url) {
     | def say() { println(url) }
     | }
defined class Foo

scala> new Foo("urlvalue").say
urlvalue

If we introduce some confusion over the case of the constructor parameter this example still works as expected:

scala> class Bar(val Url: String)
defined class Bar

scala> class Foo(url: String) extends Bar(url) {
     | def say() { println(url) }
     | }
defined class Foo

scala> new Foo("urlvalue").say
urlvalue

Interestingly you might think that this has worked because it has introduced a lower-case url field in Foo in addition to the upper case Url in Bar, but that doesn't seem to be the case - the compiler seems to be smart enough to know that it can go to Url to get the value of url in say, as no lower case field is generated.

scala> :javap -private Bar
Compiled from "<console>"
public class Bar extends java.lang.Object implements scala.ScalaObject{
    private final java.lang.String Url;
    public java.lang.String Url();
    public Bar(java.lang.String);
}

scala> :javap -private Foo
Compiled from "<console>"
public class Foo extends Bar implements scala.ScalaObject{
    public void say();
    public Foo(java.lang.String);
}

The only time I can see that this gets confusing is if you mis-spell a var field. In this case you do actually introduce a new field, and the two can get out of step.

scala> class Bar(var Url: String)
defined class Bar

scala> class Foo(url: String) extends Bar(url) {
     | def say() { println(url) }
     | }
defined class Foo

scala> val f = new Foo("urlvalue")
f: Foo = Foo@64fb7efa

scala> f.say
urlvalue

scala> f.Url = "newvalue"
f.Url: String = newvalue

scala> f.say
urlvalue

scala> :javap -private Foo
Compiled from "<console>"
public class Foo extends Bar implements scala.ScalaObject{
    private final java.lang.String url;
    public void say();
    public Foo(java.lang.String);
}
Duncan McGregor
  • 17,665
  • 12
  • 64
  • 118
  • 1
    "I'm having trouble finding fault with the way that this works." -- because it always assume I am perfect being and I don't make typos. Constructor arguments should be what they are -- just arguments, if anyone would like them to be visible in entire class could use the appropriate means for them -- declaring them as class fields (for example). Reversing the argument -- Scala does not offer any help against such typos, except for naming convention. Considering in other places Scala is pretty strict, it is surprising -- even ugly "this_is_just_arg" :-) keyword would be helpful. – greenoldman Dec 05 '11 at 09:09