2

Edit: The bug which prompted this question has now been fixed.


In the Scala Reference, I can read (p. 86):

The interpretation of an assignment to a simple variable x = e depends on the definition of x. If x denotes a mutable variable, then the assignment changes the current value of x to be the result of evaluating the expression e. The type of e is expected to conform to the type of x. If x is a parameterless function defined in some template, and the same template contains a setter function x_= as member, then the assignment x = e is interpreted as the invocation x_=(e) of that setter function. Analogously, an assignment f .x = e to a parameterless function x is interpreted as the invocation f.x_=(e).

So, for instance, something like this works fine:

class A {
  private var _a = 0
  def a = _a
  def a_=(a: Int) = _a = a
}

I can then write

val a = new A
a.a = 10

But if I define the class like this, adding a type parameter to method a:

class A {
  private var _a = 0
  def a[T] = _a
  def a_=(a: Int) = _a = a
}

then it doesn't work any more; I get an error: reassignment to val if I write a.a = 10. Funny enough, it still works with no type parameter and an implicit parameter list, for instance.

Arguably, in this example, the type parameter is not very useful, but in the design of DSLs, it would be great to have the setter method called even if the getter has type parameters (and by the way, adding type parameters on the setter is allowed and works fine).

So I have three questions:

  1. Is there a workaround?
  2. Should the current behavior be considered a bug?
  3. Why does the compiler enforce a getter method to allow using the syntactic sugar for the setter?

UPDATE

Here's what I'm really trying to do. It's rather long, sorry, I meant to avoid it but I realized it was more confusing to omit it.

I'm designing GUIs with SWT in Scala, and having huge fun using Dave Orme's XScalaWT, which immensely reduces the amount of needed code. Here's an example from his blog post on how to create an SWT Composite that converts °C to °F degrees:

var fahrenheit: Text = null
var celsius: Text = null

composite(
  _.setLayout(new GridLayout(2, true)),

  label("Fahrenheit"),
  label("Celsius"),

  text(fahrenheit = _),
  text(celsius = _),

  button(
    "Fahrenheit => Celsius",
    {e : SelectionEvent => celcius.setText((5.0/9.0) * (fahrenheit - 32)) }
  ),
  button(
    "Celsius -> Fahrenheit",
    {e : SelectionEvent => fahrenheit.setText((9.0/5.0) * celsius + 32) })
  )
)

The argument to each of the widget-constructing methods is of type (WidgetType => Any)*, with a few useful implicit conversions, which for instance allow to directly specify a string for widgets that have a setText() method. All constructor functions are imported from a singleton object.

In the end, I'd like to be able to write something along these lines:

val fieldEditable = new WritableValue // observable value

composite(
  textField(
    editable <=> fieldEditable,
    editable = false
  ),
  checkbox(
    caption = "Editable",
    selection <=> fieldEditable
  )
)

This would bind the editable property of the textfield to the selection of the checkbox through the WritableValue variable.

First: named arguments are not applicable here, so the line editable = false has to come from somewhere. So, along the widget-constructing methods in the singleton object, I could write, conceptually,

def editable_=[T <: HasEditable](value: Boolean) = (subject: T) => subject.setEditable(value)

... but this works only if the getter is also present. Great: I'd need the getter anyway in order to implement databinding with <=>. Something like this:

def editable[T <: HasEditable] = new BindingMaker((widget: T) => SWTObservables.observeEditable(widget))

If this worked, life would be good because I can then define <=> in BindingMaker and I can use this nice syntax. But alas, the type parameter on the getter breaks the setter. Hence my original question: why would this simple type parameter affect whether the compiler decides to go ahead with the syntactic sugar for calling the setter?

I hope this makes it a bit clearer now. Thanks for reading…

Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
  • Can you be more explicit about the role of your genreic type? I don't see how it can be useful in a getter. – Nicolas Feb 11 '11 at 09:55
  • Admittedly, it's not useful in this example. I'm trying to improve on an SWT/JFace/Databinding DSL where this would allow for nicer syntax if this particular example worked. – Jean-Philippe Pellet Feb 11 '11 at 10:08
  • 1
    Perhaps it might be useful if you gave an example of the situation where you really expect it to be useful. I'm having trouble imagining it... – Submonoid Feb 11 '11 at 10:20
  • I still don't see why you need generic on your setter since you don't use it. Is it only to be coherent with the getter? – Nicolas Feb 11 '11 at 12:28
  • @Nicolas Oops, I corrected my getter, which was using `Text` instead of `T`, sorry. – Jean-Philippe Pellet Feb 11 '11 at 12:37
  • Ok, It makes sense now. Even if your setter and getter does not look to get or set anything. :) – Nicolas Feb 11 '11 at 12:40

1 Answers1

4

UPDATE Deleted the entire previous answer in light of new information.

There's a lot of very odd stuff going on here, so I'm going try try and explain my understanding of what you have so far:

def editable_=[T <: HasEditable](value: Boolean) = (subject: T) => subject.setEditable(value)

This is a setter method, and exists purely so that it can give the appearance of beinng a named parameter in your DSL. It sets nothing and actually returns a Function.

textField(
  editable <=> fieldEditable,
  editable = false
)

This is calling the textField factory method, with what looks like a named param, but is actually the setter method defined previously.

Amazingly, the approach seems to work, despite my initial concern that the compiler would recognize this as a named parameter and produce a syntax error. I tested it with simple monomorphic (non-generic) methods, though it does require the getter method to be defined for the setter to be seen as such - a fact that you've already noted.

Some amount of "cleverness" is often required in writing a DSL (where it would otherwise be totally forbidden), so it's no surprise that your original intent was unclear. This is perhaps a completely new technique never before seen in Scala. The rules for setter and getter definitions were based on using them as getters and setters, so don't be surprised if things crack a little when you push at the boundaries like this.

It seems the real problem here is the way you're using type params. In this expression:

def editable_=[T <: HasEditable](value: Boolean) = (subject: T) => subject.setEditable(value)

The compiler has no way of inferring a particular T from the supplied argument, so it will take the most general type allowed (HasEditable in this case). You could change this behaviour by explicitly supplying a type param when using the method, but that would seem to defeat the entire point of what you're seeking to achieve.

Given that functions can't be generic (only methods can), I doubt that you even want type bounds at all. So one approach you could try is to just drop them:

def editable_=(value: Boolean) = (subject: HasEditable) => subject.setEditable(value)
def editable = new BindingMaker((widget: HasEditable) => SWTObservables.observeEditable(widget))
Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
  • I don't want a getter and a setter for generic properties; I'm building a DSL and I'm trying to use this form of syntactic sugar, so this doesn't answer the question (admittedly, my example is very simplified and does not illustrate how this could be useful). As for the underscore: I was just following the Scala Style Guide, p. 9 (http://www.codecommit.com/scala-style-guide.pdf) – Jean-Philippe Pellet Feb 11 '11 at 10:03
  • An example of what you're *actually* trying to do might be of some help here then! – Kevin Wright Feb 11 '11 at 10:15
  • The style guide mentions, on page 5, that an underscore prefix is "one of the less error-prone conventions" without actually advocating it. That guide was written by Daniel Spiewak, and you can see his general position on underscores in names in [his comment to my response here](http://stackoverflow.com/questions/4354261/why-do-people-use-as-an-identifier-suffix/4354325#4354325) – Kevin Wright Feb 11 '11 at 10:22
  • Kevin, thanks a lot for you new answer! You nailed it. Moving the type parameter from the method to the type annotation of the parameter of the returned function does the trick, and keeps complete type safety. Great! Now, this example apart: I's still say that, for consistency's sake, it should work even if the getter, for some reason, has a type parameter, wouldn't you agree? – Jean-Philippe Pellet Feb 14 '11 at 13:31
  • Anyway, I have reported it on Trac: https://lampsvn.epfl.ch/trac/scala/ticket/4237. Let's see whether or not the Scala team decides to fix this. – Jean-Philippe Pellet Feb 14 '11 at 13:34
  • It doesn't make sense to me that a getter should take a type parameter. It's fair enough to use some type defined in the containing class, but type params on any method are only really useful if they can be inferred somehow, which just isn't possible on a getter where we have no parameters to drive that inference. – Kevin Wright Feb 16 '11 at 09:36
  • Looks like this bug has just been fixed: https://issues.scala-lang.org/browse/SI-4237 – Jean-Philippe Pellet May 31 '11 at 09:06