[SIP-15] Value classes and "new" don't mix

158 views
Skip to first unread message

Simon Ochsenreither

unread,
May 14, 2012, 11:14:55 PM5/14/12
to scala...@googlegroups.com
At least in my opinion. Can we get rid of "new" at least in the syntax?

Requiring "new" for value classes feels a lot like the early design mistakes in Java, e.g. like new java.lang.Integer(1) and doesn't work well with the current style of things like BigInt(1) or BigDecimal("1.0"). The existing AnyVal classes don't support something like new Int(1), too.

In fact, the whole SIP emphasizes that the wrapping has no identity nor semantic meaning and the instance is only created as a last resort.
Using "new" for it is pretty much contradicting that. "new" is all about creating a distinct, freshly allocated instance with its own identity and guarantees that there is _really_ a new, separate instance created.

I think we should be really consistent here and drop the "new", so that
ValueClass("Foo")
desugars to a either
a) a constructor invocation (if there is no apply in the companion object)
b) a call to apply in the companion object which invokes a private constructor (if there is an apply).

(I know there is "case" and the possibility to write an additional apply method, but I think developers should be correct-by-default and not correct-with-additional-effort.)

What do you think?

Thanks,

Simon

Aleksey Nikiforov

unread,
May 14, 2012, 11:30:12 PM5/14/12
to scala...@googlegroups.com
All I can say is... indeed.
+1

martin odersky

unread,
May 15, 2012, 2:45:56 AM5/15/12
to scala...@googlegroups.com
On Tue, May 15, 2012 at 5:14 AM, Simon Ochsenreither <simon.och...@googlemail.com> wrote:
At least in my opinion. Can we get rid of "new" at least in the syntax?

Requiring "new" for value classes feels a lot like the early design mistakes in Java, e.g. like new java.lang.Integer(1) and doesn't work well with the current style of things like BigInt(1) or BigDecimal("1.0"). The existing AnyVal classes don't support something like new Int(1), too.

In fact, the whole SIP emphasizes that the wrapping has no identity nor semantic meaning and the instance is only created as a last resort.
Using "new" for it is pretty much contradicting that. "new" is all about creating a distinct, freshly allocated instance with its own identity and guarantees that there is _really_ a new, separate instance created.

I think we should be really consistent here and drop the "new", so that
ValueClass("Foo")
desugars to a either
a) a constructor invocation (if there is no apply in the companion object)
b) a call to apply in the companion object which invokes a private constructor (if there is an apply).

It's technically impossible as things stand. We have to generate the apply very early, so that every use site can see it. But we see the fact that we have a value class only later, when we analyze its parents and see that AnyVal is among them.

Cheers

 - Martin





(I know there is "case" and the possibility to write an additional apply method, but I think developers should be correct-by-default and not correct-with-additional-effort.)

What do you think?

Thanks,

Simon



--
Martin Odersky
Prof., EPFL and Chairman, Typesafe
PSED, 1015 Lausanne, Switzerland
Tel. EPFL: +41 21 693 6863
Tel. Typesafe: +41 21 691 4967

Simon Ochsenreither

unread,
May 15, 2012, 3:24:00 AM5/15/12
to scala...@googlegroups.com
It's technically impossible as things stand. We have to generate the apply very early, so that every use site can see it. But we see the fact that we have a value class only later, when we analyze its parents and see that AnyVal is among them.

My rule was just that over-complicated because I hoped to "save" one indirection. I agree that the implementation complexity isn't worth it.

Let's just always add the apply and make the constructor private.

martin odersky

unread,
May 15, 2012, 3:27:57 AM5/15/12
to scala...@googlegroups.com
Even that we can't do as things stand. It's in my explanation.

Cheers

 - Martin

Andrés Adolfo Testi

unread,
May 15, 2012, 8:36:38 AM5/15/12
to scala...@googlegroups.com
+1

Also, I think that value classes should be case classes by default. I'm not an FP expert, but my feeling is that "Value" is something related to "Algebraic data type".
Regards.

- Andrés

Daniel Sobral

unread,
May 15, 2012, 1:07:27 PM5/15/12
to scala...@googlegroups.com
Note that an AnyVal can still be boxed, depending on the
circumstances. Besides, it would complicate the life of someone
consuming the class: he or she would have to pay attention to the
*parents* of a class, to check how to instantiate an element of it.
Fine if you are consuming your own classes, not so fine otherwise.
--
Daniel C. Sobral

I travel to the future all the time.

Aleksey Nikiforov

unread,
May 15, 2012, 1:11:10 PM5/15/12
to scala...@googlegroups.com
You alraedy have to pay attention to the "parents" of a class, because .isInstanceOf[] is not guaranteed to work when the value is not wrapped. Removing "new" makes this stand out more.

Simon Ochsenreither

unread,
May 15, 2012, 1:22:02 PM5/15/12
to scala...@googlegroups.com
I don't understand that.

What would be the situation where you would say:

new ValueClass(1) shows more of my intent compared to ValueClass(1)?”

I argue, that this situation can never arise, because the spec and the compiler are trying very hard to do the exact opposite thing.

Having
new ValueClass(1) is in my opinion pretty much contradicting to what value classes are all about.

Simon Ochsenreither

unread,
May 15, 2012, 1:26:33 PM5/15/12
to scala...@googlegroups.com
I pretty much think value classes imply case by default:

case classes synthesize a lot of those methods which are synthesized for value classes, too. It is just that the implementation is different. Just add an apply to it and be consistent.

martin odersky

unread,
May 15, 2012, 1:31:04 PM5/15/12
to scala...@googlegroups.com


On Tue, May 15, 2012 at 7:26 PM, Simon Ochsenreither <simon.och...@googlemail.com> wrote:
I pretty much think value classes imply case by default:

case classes synthesize a lot of those methods which are synthesized for value classes, too. It is just that the implementation is different. Just add an apply to it and be consistent.

As I have already written, we can't. A case class is known as such to the parser by the presence of the keyword `case`. A value class is only known
by the fact that it's parent is AnyVal, which requires name resolution and type analysis to determine. I have already explained that it is not possible to reconcile that with automatic apply generation. You'd need to invent special syntax for value classes. 

Personally, I think case classes present already enough entanglement of features. No need to extend that to other types of classes; in particular ones that exist primarily for performance. Remember, one of the primary use cases of a value class is as implicit wrappers. I need none of the case class methods for such a wrapper, so why insist on generating them?

Cheers

- Martin

Simon Ochsenreither

unread,
May 15, 2012, 2:51:39 PM5/15/12
to scala...@googlegroups.com
Hi Martin,



As I have already written, we can't. A case class is known as such to the parser by the presence of the keyword `case`. A value class is only known
by the fact that it's parent is AnyVal, which requires name resolution and type analysis to determine.

Ah right, yes. Forgot that.

 
Personally, I think case classes present already enough entanglement of features. No need to extend that to other types of classes; in particular ones that exist primarily for performance. Remember, one of the primary use cases of a value class is as implicit wrappers. I need none of the case class methods for such a wrapper, so why insist on generating them?

Sorry, I wasn't clear enough. My comparison between case and AnyVal was intended to show that they both have special rules to what happens behind the scenes to stuff like toString, equals, etc. and focused on some possible implementation detail.

In the end, I just think new needs to be gone, independently of the approach to achieve that.
I'm very sympathetic to “It's hard to implement in the compiler”, but this should never have an impact when deciding what's the correct thing to do for the user.

Pretty much every “we did it this way because it was easier to implement for us” in language design history is coupled with deep regrets about it and the wish to have done different.

I'm still confident that there is no actual use-case where reasonable people would say “this is the case where having new in front of my value class improves the clarity of my code and shows my actual intent”.

Thanks and bye,

Simon

Andrés Adolfo Testi

unread,
May 15, 2012, 3:34:03 PM5/15/12
to scala...@googlegroups.com
In Pascal, Modula-2, Ada, C++, C#, Java, JavaScript, etc., "new" implies "memory allocation" and returns memory references.

- Andrés

Eric Torreborre

unread,
May 15, 2012, 8:23:54 PM5/15/12
to scala...@googlegroups.com
Hi Simon,

>In the end, I just think new needs to be gone, independently of the approach to achieve that.
>I'm very sympathetic to “It's hard to implement in the compiler”, but this should never have an impact when deciding what's the correct thing to do for the user.

>Pretty much every “we did it this way because it was easier to implement for us” in language design history is coupled with deep regrets about it and the wish to have done different.

I don't agree with that. We can't ask for any kind of feature then pretend that the implementation complexity doesn't exist. "hard to implement" means "hard to maintain", "bug-prone", "longer to deliver",... I think it's perfectly valid to refuse some features when they don't blend in the implementation (unless there's an opportunity for refactoring/unification).

Eric. 

Pavel Pavlov

unread,
May 16, 2012, 12:38:42 AM5/16/12
to scala...@googlegroups.com
Hi Simon and all,

This topic was already discussed here:
https://groups.google.com/d/msg/scala-sips/VnISrKzajo4/RfUn2_CWWTQJ
One possible syntactic solution for the translation problem I've proposed there is `val class V(x: T)` instead of `class V(x: T extends AnyVal`.

BTW, I fully agree that `new ValueClass(1)` is ugly, useless and even worse, it's confusing and misleading: in virtually all mainstream OO languages since good ol' C++ days till now `new` keyword is used as a clear mark of heap memory allocation (which does not occur for value classes), creation of the object with its own identity (value classes have no identity) and solely for reference types (which are exactly opposite to value types).


среда, 16 мая 2012 г., 1:51:39 UTC+7 пользователь Simon Ochsenreither написал:

Simon Ochsenreither

unread,
May 16, 2012, 1:20:46 AM5/16/12
to scala...@googlegroups.com
The thing is that I really like the "extends AnyVal" approach. I have no better idea at the moment than telling everyone to add case to their value class so that the syntax doesn't contradict the specified semantics. This should be put into the style guide if we don't come up with a better solution.

martin odersky

unread,
May 16, 2012, 4:25:37 AM5/16/12
to scala...@googlegroups.com
On Tue, May 15, 2012 at 8:51 PM, Simon Ochsenreither <simon.och...@googlemail.com> wrote:
Hi Martin,



As I have already written, we can't. A case class is known as such to the parser by the presence of the keyword `case`. A value class is only known
by the fact that it's parent is AnyVal, which requires name resolution and type analysis to determine.

Ah right, yes. Forgot that.

 
Personally, I think case classes present already enough entanglement of features. No need to extend that to other types of classes; in particular ones that exist primarily for performance. Remember, one of the primary use cases of a value class is as implicit wrappers. I need none of the case class methods for such a wrapper, so why insist on generating them?

Sorry, I wasn't clear enough. My comparison between case and AnyVal was intended to show that they both have special rules to what happens behind the scenes to stuff like toString, equals, etc. and focused on some possible implementation detail.

In the end, I just think new needs to be gone, independently of the approach to achieve that.
I'm very sympathetic to “It's hard to implement in the compiler”, but this should never have an impact when deciding what's the correct thing to do for the user.

No, you misunderstand. I did not say "hard", I said "impossible". The compiler does not have a license to explore things at it sees fit. We would risk reporting cyclic reference errors where no true ones exist. 

I think in the end we are much better off having a more general and orthogonal scheme where things such as factories or structural hashing/equality could be derived automatically for arbitrary classes, not just value classes.

No concrete proposition on the table yet, but
I'd be happy to get a discussion going for 2.11 once 2.10 is out.

Cheers

 - Martin


Reply all
Reply to author
Forward
0 new messages