Stacking traits, case classes and copy()

1,762 views
Skip to first unread message

Nolan Darilek

unread,
Nov 14, 2012, 2:11:46 PM11/14/12
to scala...@googlegroups.com
I have some authentication code, and today I had what I thought was the
bright idea of breaking behaviors into traits. But I have a couple
places in my code where I create a User as a var, then call convenience
methods on the case class which basically do a copy().

If I have everything in my User case class, this works fine. But if I
break the class into a User which dynamically extends traits, I get an
error with the copy:

case class User(name:String = "me")

trait HasPassword {
this: User =>
}

trait RequiresEmailValidation {
this: User =>
}

var u = new User() with HasPassword with RequiresEmailValidation

u = u.copy(name = "someone else")

/home/nolan/Projects/Bazaar/trunk/test.scala:13: error: type mismatch;
found : this.User
required: this.User with this.HasPassword with
this.RequiresEmailValidation
u = u.copy(name = "someone else")
^
one error found

Is there any way around this?

I wouldn't mind making User a val, but there are places where I
dynamically initialize some value based on another (if there are 0 users
saved in the database then I add the "admin" role to this first one),
but I don't know a clean way of doing that in case class initialization,
so I store the user in a var and initialize it a bit more before saving.

Thanks.

Som Snytt

unread,
Nov 14, 2012, 8:13:32 PM11/14/12
to Nolan Darilek, scala...@googlegroups.com
On Wed, Nov 14, 2012 at 11:11 AM, Nolan Darilek <no...@thewordnerd.info> wrote:
I have some authentication code, and today I had what I thought was the bright idea of breaking behaviors into traits. But I have a couple places in my code where I create a User as a var, then call convenience methods on the case class which basically do a copy().


I think this is a similar question about abstracting over a case class copy:
http://stackoverflow.com/questions/12370244/case-class-copy-method-with-superclass/

I don't think my answer there (which no one liked) has anything to do with what I just came up with, but:

package userland

trait Userland {
  import language.implicitConversions
  trait Named { def name: String }
  trait Authd { def password: String }
  type User <: Named
  def user(n: String): User
  trait Aliaser {
    def aka(n: String): User
  }
  implicit def aliaser(u: User): Aliaser
}

trait Secureland { this: Userland =>
  type User = AuthUser
  case class AuthUser(name: String, password: String) extends Named with Authd
  override def user(n: String) = AuthUser(n, "...")
  class AuthAliaser(val u: AuthUser) extends Aliaser {
    def aka(n: String) = u.copy(name=n)
  }
  override def aliaser(u: User) = new AuthAliaser(u)
}

object Test extends App with Userland with Secureland {
  val u = user("Bob")
  val v = u aka "Admin"
  println(v)
}

Reply all
Reply to author
Forward
0 new messages