[scala] Recursive knot tying and case class extension

View: New views
11 Messages — Rating Filter:   Alert me  

[scala] Recursive knot tying and case class extension

by Meredith Gregory :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Dear Scalarazzi,

i'm pretty much in favor of not being able to extend case classes. i noticed that decision forces certain idioms for recursive knot-tying. The following is no longer a "modern" option.

scala> case class Response[Request]( magic : Int, more : String, deeper : Option[Request] )
defined class Response

scala> case class Request[Response]( magic : Int, more : String, deeper : Option[Response] )
defined class Request

scala> case class JustifiedRequest( m : Int, p : String, d : Option[Response[JustifiedRequest]] ) extends Request[Response[JustifiedRequest]]( m, p, d )
warning: there were deprecation warnings; re-run with -deprecation for details
defined class JustifiedRequest

scala> case class JustifiedResponse( m : Int, p : String, d : Option[Request[JustifiedResponse]] ) extends Response[Request[JustifiedResponse]]( m, p, d )
warning: there were deprecation warnings; re-run with -deprecation for details
defined class JustifiedResponse

scala>

Best wishes,

--greg

--
L.G. Meredith
Managing Partner
Biosimilarity LLC
1219 NW 83rd St
Seattle, WA 98117

+1 206.650.3740

http://biosimilarity.blogspot.com

Re: [scala] Recursive knot tying and case class extension

by Miles Sabin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thu, Oct 15, 2009 at 7:57 PM, Meredith Gregory
<lgreg.meredith@...> wrote:
> i'm pretty much in favor of not being able to extend case classes. i noticed
> that decision forces certain idioms for recursive knot-tying. The following
> is no longer a "modern" option.

Make Request and Response plain abstract classes ...

Cheers,


Miles

--
Miles Sabin
tel: +44 (0)7813 944 528
skype:  milessabin
http://www.chuusai.com/
http://twitter.com/milessabin

Re: [scala] Recursive knot tying and case class extension

by Colin Bullock :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

It's a bit more boiler-plate if you need to pattern match on Request and Response, but the extractors are fairly straightforward to add if need. Easy trade off in my books for being rid of the trouble of case class inheritance. :)

- Colin

Re: [scala] Recursive knot tying and case class extension

by Meredith Gregory :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Dear Miles, Colin,

scala> Request[Int]( 0, "zero", None ) match { case Request( _, msg, _ ) => msg }
res0: String = zero

scala> class ARequest[Response]( magic : Int, more : String, deeper : Option[Response] )
defined class ARequest

scala> new ARequest[Int]( 0, "zero", None ) match { case ARequest( _, msg, _ ) => msg }
<console>:8: error: not found: value ARequest
       new ARequest[Int]( 0, "zero", None ) match { case ARequest( _, msg, _ ) => msg }
                                                         ^
<console>:8: error: not found: value msg
       new ARequest[Int]( 0, "zero", None ) match { case ARequest( _, msg, _ ) => msg }
                                                                                  ^

scala>

Best wishes,

--greg

On Thu, Oct 15, 2009 at 12:09 PM, Colin Bullock <cmbullock@...> wrote:
It's a bit more boiler-plate if you need to pattern match on Request and Response, but the extractors are fairly straightforward to add if need. Easy trade off in my books for being rid of the trouble of case class inheritance. :)

- Colin



--
L.G. Meredith
Managing Partner
Biosimilarity LLC
1219 NW 83rd St
Seattle, WA 98117

+1 206.650.3740

http://biosimilarity.blogspot.com

Re: [scala] Recursive knot tying and case class extension

by Colin Bullock :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Just need an extractor in the companion:

scala> object Wrapper {                                                                    
     | class ARequest[Response](val magic: Int, val more: String, val deeper: Option[Response])
     | object ARequest {                                                                       
     | def unapply[Response](ar: ARequest[Response]) = Some((ar.magic, ar.more, ar.deeper))    
     | }}                                                                                      
defined module Wrapper

scala> import Wrapper._
import Wrapper._

scala> new ARequest[Int](0, "zero", None) match { case ARequest(_, msg, _) => msg }
res1: String = zero

Note, the Wrapper object is just to keep the class and it's companion object together for the interpreter.

- Colin

Re: [scala] Recursive knot tying and case class extension

by Meredith Gregory :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Dear Colin,

Thanks! Yes, i got the pattern before i posted. The point is that the original simple design pattern has now been eliminated in favor of something much more heavy-weight (by comparison). i see that there is a trade-off. i just hadn't noticed how much the trade-off affected my style of coding ;-) -- which includes lots of recursive knot-tying.

Best wishes,

--greg

On Thu, Oct 15, 2009 at 12:36 PM, Colin Bullock <cmbullock@...> wrote:
Just need an extractor in the companion:

scala> object Wrapper {                                                                    
     | class ARequest[Response](val magic: Int, val more: String, val deeper: Option[Response])
     | object ARequest {                                                                       
     | def unapply[Response](ar: ARequest[Response]) = Some((ar.magic, ar.more, ar.deeper))    
     | }}                                                                                      
defined module Wrapper

scala> import Wrapper._
import Wrapper._


scala> new ARequest[Int](0, "zero", None) match { case ARequest(_, msg, _) => msg }
res1: String = zero

Note, the Wrapper object is just to keep the class and it's companion object together for the interpreter.

- Colin



--
L.G. Meredith
Managing Partner
Biosimilarity LLC
1219 NW 83rd St
Seattle, WA 98117

+1 206.650.3740

http://biosimilarity.blogspot.com

Re: [scala] Recursive knot tying and case class extension

by Miles Sabin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thu, Oct 15, 2009 at 8:51 PM, Meredith Gregory
<lgreg.meredith@...> wrote:
> Dear Colin,
>
> Thanks! Yes, i got the pattern before i posted. The point is that the
> original simple design pattern has now been eliminated in favor of something
> much more heavy-weight (by comparison). i see that there is a trade-off. i
> just hadn't noticed how much the trade-off affected my style of coding ;-)
> -- which includes lots of recursive knot-tying.

I don't quite understand why you can't have your leaves be case
classes and keep everything else abstract,

Welcome to Scala version 2.8.0.r18324-b20091014231244 (Java
HotSpot(TM) Server VM, Java 1.6.0_14).
Type in expressions to have them evaluated.
Type :help for more information.

scala> abstract class Response[Request]( magic : Int, more : String,
deeper : Option[Request] )
defined class Response

scala> abstract class Request[Response]( magic : Int, more : String,
deeper : Option[Response] )
defined class Request

scala> case class ARequest[Response]( magic : Int, more : String,
deeper : Option[Response] )
defined class ARequest

scala> ARequest( 0, "zero", None ) match { case ARequest( _, msg, _ )
=> msg }
res0: String = zero

Cheers,


Miles

--
Miles Sabin
tel: +44 (0)7813 944 528
skype:  milessabin
http://www.chuusai.com/
http://twitter.com/milessabin

Re: [scala] Recursive knot tying and case class extension

by Colin Bullock :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

In my experience, I agree with Miles on this one. I've rarely run into the need to deconstruct the parent classes of some hierarchy through pattern-matching (perhaps matching on some parent trait/class as a simple type annotation occasionally); I almost always find that it's the concrete subclasses I need to match against in practice. Of course, to each their own, your mileage may vary, etc.

Having said that, a hypothetical example springs to mind where matching on a superclass could be helpful:

class NamedThing(val name: String)
case class Person(override val name: String, age: Int) extends NamedThing(name)
case class Pet(override val name: String, species: String) extends NamedThing(name)

Pet("Fido", "Dog") match { case NamedThing(name) => name }

However, in cases such as (but hopefully more complex than) this, the missing extractor is generally a trivial one-liner to add.

object NamedThing { def unapply(nt: NamedThing) = Some(nt.name) }

- Colin

Re: [scala] Recursive knot tying and case class extension

by bearfeeder :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



On Thu, Oct 15, 2009 at 1:21 PM, Colin Bullock <cmbullock@...> wrote:
In my experience, I agree with Miles on this one. I've rarely run into the need to deconstruct the parent classes of some hierarchy through pattern-matching (perhaps matching on some parent trait/class as a simple type annotation occasionally); I almost always find that it's the concrete subclasses I need to match against in practice. Of course, to each their own, your mileage may vary, etc.

Having said that, a hypothetical example springs to mind where matching on a superclass could be helpful:

class NamedThing(val name: String)
case class Person(override val name: String, age: Int) extends NamedThing(name)
case class Pet(override val name: String, species: String) extends NamedThing(name)

Pet("Fido", "Dog") match { case NamedThing(name) => name }


I do almost the same thing, except:
trait NamedThing{def name: String}

It makes:
case class Person(name: String, age: Int) extends NamedThing

cleaner

 
However, in cases such as (but hopefully more complex than) this, the missing extractor is generally a trivial one-liner to add.

object NamedThing { def unapply(nt: NamedThing) = Some(nt.name) }

- Colin



--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

Re: [scala] Recursive knot tying and case class extension

by Colin Bullock :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


I do almost the same thing, except:
trait NamedThing{def name: String}

It makes:
case class Person(name: String, age: Int) extends NamedThing

cleaner

Indeed, much nicer. All the 'override val's were bothering me just writing the email. :)

- Colin

Re: [scala] Recursive knot tying and case class extension

by Meredith Gregory :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Dear Colin, Miles, et al,

i confess: in production code i do the trait pattern DPP mentioned. In general, i don't think it's a big deal. You just can't factor certain pattern-matching behavior up above the concrete instances.

scala> trait Adjuster[T <: ((Int,Int) => Int)] {
             def checkJustification( adjustmentFormula : T )
                                               ( req : Request[Response[_]],
                                                 resp : Response[Request[_]] ) : Int = {
                   req match {
                            case Request( qm, _, _ ) =>
                                     resp match {
                                                case Response( sm, _, _ ) =>
                                                         adjustmentFormula( qm, sm )
                                              }
                          }
              }
            }
defined trait Adjuster

scala> 

The use case is one where you know that the parametric case class is really part of a big knot. Then you can safely factor pattern-matching as high up as scope will allow. The version Miles suggests will cause this behavior to have to use abstract accessors and forgo pattern-matching.

Best wishes,

--greg

On Thu, Oct 15, 2009 at 1:45 PM, Colin Bullock <cmbullock@...> wrote:

I do almost the same thing, except:
trait NamedThing{def name: String}

It makes:
case class Person(name: String, age: Int) extends NamedThing

cleaner

Indeed, much nicer. All the 'override val's were bothering me just writing the email. :)

- Colin



--
L.G. Meredith
Managing Partner
Biosimilarity LLC
1219 NW 83rd St
Seattle, WA 98117

+1 206.650.3740

http://biosimilarity.blogspot.com