- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Monkey(patch)ing around
Currently, the following code:
case class Foo(bar: String)
translates to something like:
class Foo(bar: String) { /* hashCode, toString, equals, Product stuff, accessors, etc */ }
object Foo extends (String => Foo) {
def apply(bar: String): Foo = new Foo(bar)
/* unapply */
}
Would it be possible to make the following code:
implicit case class PimpedString(private val s: String) {
def allcaps = s.map(_.toUpperCase).mkString
}
translate to something like:
class PimpedString(private val s: String) {
def allcaps = s.map(_.toUpperCase).mkString
/* ... */
}
implicit object PimpedString extends (String => PimpedString) {
def apply(s: String): PimpedString = new PimpedString(s)
/* unapply */
}
so that the following compiles and evaluates to true:
"wee".allcaps == "WEE"
It's a bit overkill... I'm sure I'm going to get bit in the ass by some code that expects a Product and makes all my implicit case classes kick in willy nilly... Or forgetting to make the case class parameter private and getting some oddly-named methods on all my strings that do nothing but return the same string... Not to mention all the bugs-in-waiting that I'm not even considering...
However... I'm probably willing to live with those risks. It makes monkeypatching every-so-slighter more concise than the current approach of defining a class and an implicit def. In fact, it's -almost- as concise as Ruby's open classes. It's also a fairly minor change, and the translation is entirely natural given the current semantics of case class.
What does everyone think?
--j
case class Foo(bar: String)
translates to something like:
class Foo(bar: String) { /* hashCode, toString, equals, Product stuff, accessors, etc */ }
object Foo extends (String => Foo) {
def apply(bar: String): Foo = new Foo(bar)
/* unapply */
}
Would it be possible to make the following code:
implicit case class PimpedString(private val s: String) {
def allcaps = s.map(_.toUpperCase).mkString
}
translate to something like:
class PimpedString(private val s: String) {
def allcaps = s.map(_.toUpperCase).mkString
/* ... */
}
implicit object PimpedString extends (String => PimpedString) {
def apply(s: String): PimpedString = new PimpedString(s)
/* unapply */
}
so that the following compiles and evaluates to true:
"wee".allcaps == "WEE"
It's a bit overkill... I'm sure I'm going to get bit in the ass by some code that expects a Product and makes all my implicit case classes kick in willy nilly... Or forgetting to make the case class parameter private and getting some oddly-named methods on all my strings that do nothing but return the same string... Not to mention all the bugs-in-waiting that I'm not even considering...
However... I'm probably willing to live with those risks. It makes monkeypatching every-so-slighter more concise than the current approach of defining a class and an implicit def. In fact, it's -almost- as concise as Ruby's open classes. It's also a fairly minor change, and the translation is entirely natural given the current semantics of case class.
What does everyone think?
--j










Re: Monkey(patch)ing around
On Thu, Jan 22, 2009 at 1:38 PM, Jorge Ortiz wrote:
> Currently, the following code:
>
> case class Foo(bar: String)
>
> translates to something like:
>
> class Foo(bar: String) { /* hashCode, toString, equals, Product stuff,
> accessors, etc */ }
> object Foo extends (String => Foo) {
> def apply(bar: String): Foo = new Foo(bar)
> /* unapply */
> }
>
> Would it be possible to make the following code:
>
> implicit case class PimpedString(private val s: String) {
> def allcaps = s.map(_.toUpperCase).mkString
> }
>
> translate to something like:
>
> class PimpedString(private val s: String) {
> def allcaps = s.map(_.toUpperCase).mkString
> /* ... */
> }
> implicit object PimpedString extends (String => PimpedString) {
> def apply(s: String): PimpedString = new PimpedString(s)
> /* unapply */
> }
It would be possible, and it would easy monkeypatching.
I am just not 100% sure yet that's a good thing...
Cheers
Re: Monkey(patch)ing around
Hi Jorge,
Jorge Ortiz wrote:
> What does everyone think?
I kind of agree, and I actually proposed much the same thing a while
back, but I'm happy living without it.
Given,
implicit case class Foo(x : Int) { ... }
I think there's a case for this translating to either:
implicit def intToFoo(x : Int) : Foo = Foo(x)
case class Foo(x : Int) { ... }
or, as you suggested,
implicit object Foo { ... }
case class Foo(x : Int) { ... }
Unfortunately, I think people might use such syntax for the purposes of
the former, when they don't actually want (or expect) the more
Pandora's-box-esque effects of the latter...
Nevertheless, I don't want to kill off the discussion early, if anyone
else has any thoughts on it.
Cheers,
Jon
Re: Monkey(patch)ing around
Yeah, this (or similar) has been discussed (at least) twice before:
http://thread.gmane.org/gmane.comp.lang.scala/3455
http://www.nabble.com/Implicit-classes-(or-encouraging-people-to-pimp-hygienically)-td18192147.html
I think my present suggestion is the most minimal (compiler/spec) change possible to get something like what I want, but ideally I'd also like to avoid the nasty object creation, as per the suggestion in the second link above.
I'm toying around with a compiler plugins to see if I can get what I want with just a plugin, but I'm way out of my depth in all this Symbol/Tree/Type stuff :)
--j
On Mon, Feb 2, 2009 at 6:54 AM, Jon Pretty <jon [dot] pretty [at] sygneca [dot] com> wrote:
Re: Monkey(patch)ing around
Hi Jorge!
Jorge Ortiz wrote:
> Hey Jon! Welcome back! Hope you're planning to stick around :)
Yeah, I certainly plan to (time permitting) :-)
> I'm toying around with a compiler plugins to see if I can get what I
> want with just a plugin, but I'm way out of my depth in all this
> Symbol/Tree/Type stuff :)
My limited experience of compiler plugins suggests to me that it might
just be possible to implement this without too much trouble, as long as
you're happy to write it as:
@something case class Foo(...
otherwise I think you might need to start messing with the parser...
Good luck!
Jon
Re: Monkey(patch)ing around
Yup, that's the approach I'm taking. No desire whatsoever to mess with the parser :)
--j
Re: Monkey(patch)ing around
On Mon, Feb 2, 2009 at 3:54 PM, Jon Pretty <jon [dot] pretty [at] sygneca [dot] com> wrote:
Re: Monkey(patch)ing around
Hi Szymon,
Szymon Jachim wrote:
> It should do both... Simple and powerful.
Sorry, I should have made this clearer: the second option implies the
first, because the companion object is a subclass of a constructor
function for that thing.
And simple and powerful is a great recipe for disaster. Like giving a
hobbit a magic ring, electing the wrong president[1] or programming in
Lisp. ;-)
Cheers,
Jon
[1] As this is an apolitical forum, I won't say who I'm thinking of.