Scala Option and flatMap - scala

I'm trying to get the hang of working "the Scala way" so I was wondering if the following code is how things should be done in this case.
So I have the entities User and Company (mapped with LiftWeb mapper). User has currentUser which contains an Option[User] and Company has currentCompany which is an Option[Company]. In order to compare if the current user is the owner of the current company I'm doing something like:
Company.currentCompany.map{_.owner.get == User.currentUser.map{_.id.get}.openOr(-1) }.openOr(false)
It works but somehow it feels kinda verbose. Is it good? Is it not? Any better ideas?
Thanks!

Have you looked at using for-comprehensions? You could do something like the following:
for(
company <- Company.currentCompany.map{_.owner};
user <- User.currentUser.map{_.id}
) yield (company == user).getOrElse(false)
This will return true if Company.currentCompany is Some[value], and User.currentCompany is Some[value], and company.owner == user.id.
I feel there should be some way of getting rid of that getOrElse on the end, and returning the unwrapped boolean directly, hopefully someone else might be able to shed some light on this!

Using for-comprehension is definitively the solution, actually... or flatMap but less readable
To recall every generators are bound using flatMap function of the Monadic Option, except the last which is mapped (like any for and yield). Here is a good slideshow on the underneath concepts Monad
So the for comprehension is used to pass through all steps while they aren't encoded in the fail state (None for Option).
Here is a full example with four tests (the four basic cases) for each options (for and flatMap)
case class User(id: String) {
}
object User {
def currentUser(implicit me: Option[User]): Option[User] = me
}
case class Company(owner: Option[User]) {
}
object Company {
def currentCompany(implicit myCompany: Option[Company]): Option[Company] = myCompany
}
object Test extends App {
test1()
test2()
test3()
test4()
test5()
test6()
test7()
test8()
def test1() {
implicit val me: Option[User] = None
implicit val myCompany: Option[Company] = None
val v: Boolean = (for {
c <- Company.currentCompany
u <- User.currentUser
o <- c.owner if o.id == u.id
} yield true) getOrElse false
println(v)
}
def test2() {
implicit val me: Option[User] = Some(User("me"))
implicit val myCompany: Option[Company] = None
val v: Boolean = (for {
c <- Company.currentCompany
u <- User.currentUser
o <- c.owner if o.id == u.id
} yield true) getOrElse false
println(v)
}
def test3() {
implicit val me: Option[User] = None
implicit val myCompany = Some(Company(me))
val v: Boolean = (for {
c <- Company.currentCompany
u <- User.currentUser
o <- c.owner if o.id == u.id
} yield true) getOrElse false
println(v)
}
def test4() {
implicit val me: Option[User] = Some(User("me"))
implicit val myCompany = Some(Company(me))
val v: Boolean = (for {
c <- Company.currentCompany
u <- User.currentUser
o <- c.owner if o.id == u.id
} yield true) getOrElse false
println(v)
}
def test5() {
implicit val me: Option[User] = None
implicit val myCompany: Option[Company] = None
val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false
println(v)
}
def test6() {
implicit val me: Option[User] = Some(User("me"))
implicit val myCompany: Option[Company] = None
val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false
println(v)
}
def test7() {
implicit val me: Option[User] = None
implicit val myCompany = Some(Company(me))
val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false
println(v)
}
def test8() {
implicit val me: Option[User] = Some(User("me"))
implicit val myCompany = Some(Company(me))
val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false
println(v)
}
}

Given:
Case class user(name:String)
Case class company(owner:Option[User])
Val currentcompany=company(Some("Karl"))
Val currentuser=Some(user("Karl"))
Possible solution:
currentcompany.foldLeft(false) {
case (a,b) => currentuser.isDefined && b.owner == currentUser.get
}

Related

Scala for comprehension with Future, List and Option

I am building a reactive site in Scala and Play Framework, and my data models are such that I often need to compose Future and Option, and build Future of List / Set from previous values to get the result I need.
I wrote a simple app with a fake data source that you can copy and paste and it should compile. My question is, how can I get the result back, in my case UserContext, in a consumable form. Currently, I am getting back Future[Option[Future[UserContext]]].
I want to do this in pure Scala to learn the language better, so I am avoiding Scalaz at the moment. Although I know I should eventually use that.
package futures
import scala.concurrent.{Future, ExecutionContext}
// http://www.edofic.com/posts/2014-03-07-practical-future-option.html
case class FutureO[+A](future: Future[Option[A]]) extends AnyVal {
def flatMap[B](f: A => FutureO[B])(implicit ec: ExecutionContext): FutureO[B] = {
FutureO {
future.flatMap { optA =>
optA.map { a =>
f(a).future
} getOrElse Future.successful(None)
}
}
}
def map[B](f: A => B)(implicit ec: ExecutionContext): FutureO[B] = {
FutureO(future.map(_ map f))
}
}
// ========== USAGE OF FutureO BELOW ============= \\
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object TeamDB {
val basketballTeam = Team(id = 111, player_ids = Set(111, 222))
val baseballTeam = Team(id = 222, player_ids = Set(333))
def findById(teamId: Int): Future[Option[Team]] = Future.successful(
teamId match {
case 111 => Some(basketballTeam)
case 222 => Some(baseballTeam)
case _ => None
}
)
}
object PlayerDB {
val basketballPlayer1 = Player(id = 111, jerseyNumber = 23)
val basketballPlayer2 = Player(id = 222, jerseyNumber = 45)
val baseballPlayer = Player(id = 333, jerseyNumber = 5)
def findById(playerId: Int): Future[Option[Player]] = Future.successful(
playerId match {
case 111 => Some(basketballPlayer1)
case 222 => Some(basketballPlayer2)
case 333 => Some(baseballPlayer)
case _ => None
}
)
}
object UserDB {
// user1 is on BOTH the baseball and basketball team
val user1 = User(id = 111, name = "Michael Jordan", player_ids = Set(111, 333), team_ids = Set(111, 222))
// user2 is ONLY on the basketball team
val user2 = User(id = 222, name = "David Wright", player_ids = Set(222), team_ids = Set(111))
def findById(userId: Long): Future[Option[User]] = Future.successful(
userId match {
case 111 => Some(user1)
case 222 => Some(user2)
case _ => None
}
)
}
case class User(id: Int, name: String, player_ids: Set[Int], team_ids: Set[Int])
case class Player(id: Int, jerseyNumber: Int)
case class Team(id: Int, player_ids: Set[Int])
case class UserContext(user: User, teams: Set[Team], players: Set[Player])
object FutureOptionListTest extends App {
val result = for {
user <- FutureO(UserDB.findById(userId = 111))
} yield for {
players: Set[Option[Player]] <- Future.traverse(user.player_ids)(x => PlayerDB.findById(x))
teams: Set[Option[Team]] <- Future.traverse(user.team_ids)(x => TeamDB.findById(x))
} yield {
UserContext(user, teams.flatten, players.flatten)
}
result.future // returns Future[Option[Future[UserContext]]] but I just want Future[UserContext] or UserContext
}
You have created FutureO which combines the effects of Future and Option (if you are looking into Scalaz this compares with OptionT[Future, ?]).
Remembering that for ... yield is analogous to FutureO.map, the result type will always be FutureO[?] (and Future[Option[?]] if you do result.future).
The problem is you want to return a Future[UserContex] instead of a Future[Option[UserContext]]. Essentially you want to loose the Option context, so somewhere you need to explicitly handle if the user exists or not.
A possible solution in this case could be to leave out the FutureO since you are only using it once.
case class NoUserFoundException(id: Long) extends Exception
// for comprehension with Future
val result = for {
user <- UserDB.findById(userId = 111) flatMap (
// handle Option (Future[Option[User]] => Future[User])
_.map(user => Future.successful(user))
.getOrElse(Future.failed(NoUserFoundException(111)))
)
players <- Future.traverse(user.player_ids)(x => PlayerDB.findById(x))
teams <- Future.traverse(user.team_ids)(x => TeamDB.findById(x))
} yield UserContext(user, teams.flatten, players.flatten)
// result: scala.concurrent.Future[UserContext]
If you had multiple functions returning a Future[Option[?]], you probably would like to use FutureO, in this case you could create an extra function Future[A] => FutureO[A], so you can use your functions in the same for comprehension (all in the FutureO monad):
def liftFO[A](fut: Future[A]) = FutureO(fut.map(Some(_)))
// for comprehension with FutureO
val futureO = for {
user <- FutureO(UserDB.findById(userId = 111))
players <- liftFO(Future.traverse(user.player_ids)(x => PlayerDB.findById(x)))
teams <- liftFO(Future.traverse(user.team_ids)(x => TeamDB.findById(x)))
} yield UserContext(user, teams.flatten, players.flatten)
// futureO: FutureO[UserContext]
val result = futureO.future flatMap (
// handle Option (Future[Option[UserContext]] => Future[UserContext])
_.map(user => Future.successful(user))
.getOrElse(Future.failed(new RuntimeException("Could not find UserContext")))
)
// result: scala.concurrent.Future[UserContext]
But as you can see, you will always need to handle the "option context" before you can return a Future[UserContext].
To expand on Peter Neyens' answer, often I'll put a bunch of monad -> monad transformations in a special implicit class and import them as I need them. Here we have two monads, Option[T] and Future[T]. In this case, you are treating None as being a failed Future. You could probably do this:
package foo {
class OptionOps[T](in: Option[T]) {
def toFuture: Future[T] = in match {
case Some(t) => Future.successful(t)
case None => Future.failed(new Exception("option was none"))
}
}
implicit def optionOps[T](in: Option[T]) = new OptionOps[T](in)
}
Then you just import it import foo.optionOps
And then:
val a: Future[Any] = ...
val b: Option[Any] = Some("hi")
for {
aFuture <- a
bFuture <- b.toFuture
} yield bFuture // yields a successful future containing "hi"

Create a generalize method without losing types

we have
private def insertUpdateDeleteFormDsList(dsList : List[FormDefinitionDataSourceRequestModel])(implicit formDefId:Int,subject:Subject,session: Session) : (List[(Int,Int)],Seq[FormDefinitionDataSourceRequestModel],Seq[FormDefinitionDataSourceRequestModel]) = {
val incomingIds = dsList.map( ds => (ds.dataSourceId,ds.dsTypeId) )
val existingIds = formDefinitionDatasources.filter(_.tenantId === subject.tenantId).filter(_.formDefId === formDefId).map( ds => (ds.dataSourceId,ds.dataSourceTypeId) ).list
val idsForDeletion = existingIds diff incomingIds
val idsForInsertion = incomingIds diff existingIds
val idsForUpdate = existingIds diff idsForDeletion
val insertList = dsList.flatMap{ t => idsForInsertion collectFirst{ case (dsId,dsType) if(dsId == t.dataSourceId && dsType == t.dsTypeId)=> t} }
val updateList = dsList.flatMap{t=>idsForUpdate collectFirst {case (dsId,dsType) if(dsId == t.dataSourceId && dsType == t.dsTypeId)=> t}}
(idsForDeletion,updateList,insertList)
}
And other similar methods as
private def insertUpdateDelDataInstances(instances: List[Instance])(implicit subject: Subject, session: Session): (Seq[Instance], Seq[Instance], Seq[Instance]) = {
val incomingIds = instances.map(_.id)
val existingIds = dataSourceInstanceNew.filter(_.tenantId === subject.tenantId).map(_.id).list
val idsForDeletion = existingIds diff incomingIds
val idsForInsertion = incomingIds diff existingIds
val idsForUpdate = existingIds diff idsForDeletion
val deleteList = instances.flatMap{ t => idsForDeletion collectFirst{ case id if(id == t.id)=> t} }
val insertList = instances.flatMap{ t => idsForInsertion collectFirst{ case id if(id == t.id)=> t} }
val updateList = instances.flatMap{t=>idsForUpdate collectFirst {case id if(id === t.id)=> t}}
(deleteList,updateList,insertList)
}
There are similar method occurrences elsewhere. Every time List[T] will be passed as a method argument where T is always a case class. now how val incomingIds gets constructed is dependent on specific case class attributes.
We want to create a generalized function which can accept a List[T] and possibly incomingIds and return a desired tuple to avoid writing similar looking boilerplate every time.
If say logic was to "always" use T case class 's id attribute then I can easily create a parent trait with id and have all case classes mixin the trait - but that's not the case here. preparing val incomingIds depend on different case class attributes depending on where in the code they are called from.
Illustrating below
def generalizedInsertUpdateDeleteList[T](data:List[T],incomingIds:List[Int], existingIds:List[Int] )(implicit subject: Subject, session:Session) = {
val idsForDeletion = existingIds diff incomingIds
val idsForInsertion = incomingIds diff existingIds
val idsForUpdate = existingIds diff idsForDeletion
/*
//what's the best way to generalize comparison inside collectFirst?
//to use case class attribute names from `T`. Was thinking if Structural type can help but not sure if that
//can quite work unless there is a way to pass in arguments in a structural type?
val deleteList = data.flatMap{ t => idsForDeletion collectFirst{ case id if(id == t.id)=> t} }
val insertList = data.flatMap{ t => idsForInsertion collectFirst{ case id if(id == t.id)=> t} }
val updateList = data.flatMap{ t => idsForUpdate collectFirst {case id if(id === t.id)=> t}}
*/
Can shapeless help here if no other cleaner way to achieve this using standard scala/scalaz APIs?
Shapeless's records provide a type-safe way to abstract over case classes with a particular member name. For example:
import shapeless._, ops.record.Selector
def getId[A, R <: HList](a: A)(implicit
gen: LabelledGeneric.Aux[A, R],
sel: Selector[R, Witness.`'id`.T]
): sel.Out = sel(gen.to(a))
And then:
scala> case class Foo(id: String)
defined class Foo
scala> case class Bar(id: Int, name: String)
defined class Bar
scala> getId(Foo("12345"))
res0: String = 12345
scala> getId(Bar(123, "bar"))
res1: Int = 123
If you need to constrain the type of the id member, you can use Selector.Aux:
def getIntId[A, R <: HList](a: A)(implicit
gen: LabelledGeneric.Aux[A, R],
sel: Selector.Aux[R, Witness.`'id`.T, Int]
): Int = sel(gen.to(a))
Now getIntId(Bar(123, "bar")) will compile, but getIntId(Foo("12345")) won't.
You could create a type class with a PartialFunction which can be used inside collectFirst.
trait IUD[T, IdType] {
// returns a partial function which will be used in collectFirst
def collectId(t: T): PartialFunction[IdType, T]
}
We can create IUD instances for your two methods :
// I chose (Long, Long) as type of (ds.dataSourceId,ds.dsTypeId)
type FormModel = FormDefinitionDataSourceRequestModel
implicit object FormModelIUD extends IUD[FormModel, (Long, Long)] {
def collectId(t: FormModel): PartialFunction[(Long, Long), FormModel] = {
case (dsId,dsType) if(dsId == t.dataSourceId && dsType == t.dsTypeId) => t
}
}
implicit object InstanceIUD extends IUD[Instance, Int] {
def collectId(t: Instance): PartialFunction[Int, Instance] = {
case id if id == t.id => t
}
}
We can use the IUD type class in your generalizedInsertUpdateDeleteList method :
def generalizedIUDList[T, IdType](
data: List[T], incomingIds: List[IdType], existingIds: List[IdType]
)(implicit
subject: Subject, session: Session, iud: IUD[T, IdType]
) = {
val idsForDeletion = existingIds diff incomingIds
val idsForInsertion = incomingIds diff existingIds
val idsForUpdate = existingIds diff idsForDeletion
def filterIds(ids: List[IdType]) =
data.flatMap(instance => ids collectFirst(iud.collectId(instance)) )
val deleteList = filterIds(idsForDeletion)
val insertList = filterIds(idsForInsertion)
val updateList = filterIds(idsForUpdate)
(deleteList,updateList,insertList)
}
collectFirst accepts a PartialFunction, in your case PartialFunction[Int, T] I think?
You could pass the partial functions in as parameters to your generalizedInsertUpdateDeleteList method, then only these would need to be defined each time.

In Scala/Slick, how to refactor codes (respond interception) like this?

Here are the codes:
def find(loginInfo: LoginInfo): Future[Option[UserProfile]] = {
val res = DB.withSession { implicit session =>
//if loginInfo.providerID == "waylens"
userProfileTable.filter(u => u.userName === loginInfo.providerKey).list
}
val size = res.size
if (size <= 1)
Future(res.headOption.map(userProfileRecordToUserProfile))
else
throw new Exception("db error")
}
def findByEmail(providerID: String, email: String): Future[Option[UserProfile]] = {
val res = DB.withSession { implicit session =>
//if loginInfo.providerID == "waylens"
userProfileTable.filter(u => u.email === email && u.status === 1).list
}
val size = res.size
if (size <= 1)
Future(res.headOption.map(userProfileRecordToUserProfile))
else
throw new Exception("db error")
}
def findByProfileID(profileID: Long): Future[Option[UserProfile]] = {
val res = DB.withSession { implicit session =>
userProfileTable.filter(u => u.id === profileID).list
}
val size = res.size
if (size <= 1)
Future(res.headOption.map(userProfileRecordToUserProfile))
else
throw new Exception("db error")
}
These codes appeared many times, which is really annoying
val size = res.size
if (size <= 1)
Future(res.headOption.map(userProfileRecordToUserProfile))
else
throw new Exception("db error")
Does anyone have ideas about refactoring this in Slick?
You can pretty easily use high order functions here, first let's get your queries out of the methods:
lazy val a = userProfileTable.filter(u => u.userName === loginInfo.providerKey)
lazy val b = userProfileTable.filter(u => u.email === email && u.status === 1)
lazy val c = userProfileTable.filter(u => u.id === profileID)
Note that I'm making this lazy because you don't have a session in scope yet.
Now let's create a function to execute this queries (you can make this even more generic using a type parameter):
def executeQuery(q: ⇒ : Query[UserProfiles, UserProfiles#TableElementType]): Int = {
db.withSession { implicit session ⇒
q.list
}
}
Then we need to check the length:
def listToUserProfile(list: List[UserProfile]): Future[Option[UserProfile]] = {
if (list.length <= 1)
Future(list.headOption.map(userProfileRecordToUserProfile))
else
throw new Exception("db error")
}
Now you could do something like:
listToUserProfile(executeQuery(a))
There may be some error because your code is not runnable and I couldn't compile it.
First create a generic method to execute the queries which either returns a future or an error using Either
def getElement[E, U, C[_]](query: Query[E, U, C]) = {
db.withSession { implicit session =>
query.list.headOption match {
case Some(ele) => Right(Future(ele))
case None => Left(new Exception("db error"))
}
}
}
Now in the code, you can simply do
def find(loginInfo: LoginInfo): Future[Option[UserProfile]] =
getElement(userProfileTable.filter(u => u.userName === loginInfo.providerKey)).right.map(userProfileRecordToUserProfile)
Similarly for others:
def findByEmail(loginInfo: LoginInfo): Future[Option[UserProfile]] =
getElement( userProfileTable.filter(u => u.email === email && u.status === 1))).right.map(userProfileRecordToUserProfile)
On a side note, you are not wrapping the future over the DB call but after you get the result. DB call will be major source of blocking and not the processing of the DB result. You should look into wrapping the DB call inside the future

How to check if there's None in List[Option[_]] and return the element's name?

I have multiple Option's. I want to check if they hold a value. If an Option is None, I want to reply to user about this. Else proceed.
This is what I have done:
val name:Option[String]
val email:Option[String]
val pass:Option[String]
val i = List(name,email,pass).find(x => x match{
case None => true
case _ => false
})
i match{
case Some(x) => Ok("Bad Request")
case None => {
//move forward
}
}
Above I can replace find with contains, but this is a very dirty way. How can I make it elegant and monadic?
Edit: I would also like to know what element was None.
Another way is as a for-comprehension:
val outcome = for {
nm <- name
em <- email
pwd <- pass
result = doSomething(nm, em, pwd) // where def doSomething(name: String, email: String, password: String): ResultType = ???
} yield (result)
This will generate outcome as a Some(result), which you can interrogate in various ways (all the methods available to the collections classes: map, filter, foreach, etc.). Eg:
outcome.map(Ok(result)).orElse(Ok("Bad Request"))
val ok = Seq(name, email, pass).forall(_.isDefined)
If you want to reuse the code, you can do
def allFieldValueProvided(fields: Option[_]*): Boolean = fields.forall(_.isDefined)
If you want to know all the missing values then you can find all missing values and if there is none, then you are good to go.
def findMissingValues(v: (String, Option[_])*) = v.collect {
case (name, None) => name
}
val missingValues = findMissingValues(("name1", option1), ("name2", option2), ...)
if(missingValues.isEmpty) {
Ok(...)
} else {
BadRequest("Missing values for " + missingValues.mkString(", ")))
}
val response = for {
n <- name
e <- email
p <- pass
} yield {
/* do something with n, e, p */
}
response getOrElse { /* bad request /* }
Or, with Scalaz:
val response = (name |#| email |#| pass) { (n, e, p) =>
/* do something with n, e, p */
}
response getOrElse { /* bad request /* }
if ((name :: email :: pass :: Nil) forall(!_.isEmpty)) {
} else {
// bad request
}
I think the most straightforward way would be this:
(name,email,pass) match {
case ((Some(name), Some(email), Some(pass)) => // proceed
case _ => // Bad request
}
A version with stone knives and bear skins:
import util._
object Test extends App {
val zero: Either[List[Int], Tuple3[String,String,String]] = Right((null,null,null))
def verify(fields: List[Option[String]]) = {
(zero /: fields.zipWithIndex) { (acc, v) => v match {
case (Some(s), i) => acc match {
case Left(_) => acc
case Right(t) =>
val u = i match {
case 0 => t copy (_1 = s)
case 1 => t copy (_2 = s)
case 2 => t copy (_3 = s)
}
Right(u)
}
case (None, i) =>
val fails = acc match {
case Left(f) => f
case Right(_) => Nil
}
Left(i :: fails)
}
}
}
def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)
val name:Option[String] = Some("Bob")
val email:Option[String]= None
val pass:Option[String] = Some("boB")
val res = verify(List(name,email,pass))
res.fold(fail, (consume _).tupled)
val res2 = verify(List(name, Some("bob#bob.org"),pass))
res2.fold(fail, (consume _).tupled)
}
The same thing, using reflection to generalize the tuple copy.
The downside is that you must tell it what tuple to expect back. In this form, reflection is like one of those Stone Age advances that were so magical they trended on twitter for ten thousand years.
def verify[A <: Product](fields: List[Option[String]]) = {
import scala.reflect.runtime._
import universe._
val MaxTupleArity = 22
def tuple = {
require (fields.length <= MaxTupleArity)
val n = fields.length
val tupleN = typeOf[Tuple2[_,_]].typeSymbol.owner.typeSignature member TypeName(s"Tuple$n")
val init = tupleN.typeSignature member nme.CONSTRUCTOR
val ctor = currentMirror reflectClass tupleN.asClass reflectConstructor init.asMethod
val vs = Seq.fill(n)(null.asInstanceOf[String])
ctor(vs: _*).asInstanceOf[Product]
}
def zero: Either[List[Int], Product] = Right(tuple)
def nextProduct(p: Product, i: Int, s: String) = {
val im = currentMirror reflect p
val ts = im.symbol.typeSignature
val copy = (ts member TermName("copy")).asMethod
val args = copy.paramss.flatten map { x =>
val name = TermName(s"_$i")
if (x.name == name) s
else (im reflectMethod (ts member x.name).asMethod)()
}
(im reflectMethod copy)(args: _*).asInstanceOf[Product]
}
(zero /: fields.zipWithIndex) { (acc, v) => v match {
case (Some(s), i) => acc match {
case Left(_) => acc
case Right(t) => Right(nextProduct(t, i + 1, s))
}
case (None, i) =>
val fails = acc match {
case Left(f) => f
case Right(_) => Nil
}
Left(i :: fails)
}
}.asInstanceOf[Either[List[Int], A]]
}
def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)
val name:Option[String] = Some("Bob")
val email:Option[String]= None
val pass:Option[String] = Some("boB")
type T3 = Tuple3[String,String,String]
val res = verify[T3](List(name,email,pass))
res.fold(fail, (consume _).tupled)
val res2 = verify[T3](List(name, Some("bob#bob.org"),pass))
res2.fold(fail, (consume _).tupled)
I know this doesn't scale well, but would this suffice?
(name, email, pass) match {
case (None, _, _) => "name"
case (_, None, _) => "email"
case (_, _, None) => "pass"
case _ => "Nothing to see here"
}

How to parametrize Scala Slick queries by WHERE clause conditions?

Assume these two simple queries:
def findById(id: Long): Option[Account] = database.withSession { implicit s: Session =>
val query = for (a <- Accounts if a.id === id) yield a.*
query.list.headOption
}
def findByUID(uid: String): Option[Account] = database.withSession { implicit s: Session =>
val query = for (a <- Accounts if a.uid === uid) yield a.*
query.list.headOption
}
I would like to rewrite it to remove the boilerplate duplication to something like this:
def findBy(criteria: ??? => Boolean): Option[Account] = database.withSession {
implicit s: Session =>
val query = for (a <- Accounts if criteria(a)) yield a.*
query.list.headOption
}
def findById(id: Long) = findBy(_.id === id)
def findByUID(uid: Long) = findBy(_.uid === uid)
I don't know how to achieve it for there are several implicit conversions involved in the for comprehension I haven't untangled yet. More specifically: what would be the type of ??? => Boolean in the findBy method?
EDIT
These are Account and Accounts classes:
case class Account(id: Option[Long], uid: String, nick: String)
object Accounts extends Table[Account]("account") {
def id = column[Option[Long]]("id")
def uid = column[String]("uid")
def nick = column[String]("nick")
def * = id.? ~ uid ~ nick <> (Account, Account.unapply _)
}
I have this helper Table:
abstract class MyTable[T](_schemaName: Option[String], _tableName: String) extends Table[T](_schemaName, _tableName) {
import scala.slick.lifted._
def equalBy[B: BaseTypeMapper]
(proj:this.type => Column[B]):B => Query[this.type,T] = { (str:B) =>
Query[this.type,T,this.type](this) where { x => proj(x) === str} }
}
Now you can do:
val q=someTable.equalBy(_.someColumn)
q(someValue)