How to make method generic without getting "No matching Shape found" - scala

I am not sure how to get past this "No matching Shape found" error, apart from writing lots of boilerplate.
The basic idea illustrated in the Gist is that I have a very basic version of a method (works, but is very specific), then a version that takes the mapper parameter and is more generic (works too, but is specific to one particular type), and then a third version which takes a type parameter and would be very useful, but doesn't compile because of this error.
Basic method:
def updatePD_FirstNames(id: ids.PersonalDetailsId, firstNames: StringLtd30): Future[Int] = {
Better method:
def updatePD_SL(id: ids.PersonalDetailsId, mapper: tables.PersonalDetails => tables.profile.api.Rep[StringLtd30], sl: StringLtd30): Future[Int] = {
Ideal method (but doesn't compile):
def updatePD_X[X](id: ids.PersonalDetailsId, mapper: tables.PersonalDetails => tables.profile.api.Rep[X], sl: X): Future[Int] = {
```
[server] $ compile
[info] Compiling 1 Scala source to ... target\scala-2.12\classes...
[error] ...schema\DbProxy.scala:688: No matching Shape found.
[error] Slick does not know how to map the given types.
[error] Possible causes: T in Table[T] does not match your * projection,
[error] you use an unsupported type in a Query (e.g. scala List),
[error] or you forgot to import a driver api into scope.
[error] Required level: slick.lifted.FlatShapeLevel
[error] Source type: slick.lifted.Rep[X]
[error] Unpacked type: T
[error] Packed type: G
[error] val q2: Query[tables.profile.api.Rep[X], X, Seq] = q1.map(mapper)
[error] ^
[error] one error found
[error] (server/compile:compileIncremental) Compilation failed
[error] Total time: 4 s, completed 23-Mar-2017 11:15:47
```
Full code at https://gist.github.com/aholland/0845bf29d836d672d006ab58f5f1c73c

The only obvious problem I can see in the code you've posted is that X is unconstrained. It could be any type, includes ones that Slick doesn't know how to process.
What you can do is add a context bound on X. The bound you probably want is BaseTypedType, which is a "typed type" Slick uses to identify types it can work with. It's described from 11:30 in https://www.youtube.com/watch?v=tS6N5AaZTLA
You'd use it like this:
import slick.ast.BaseTypedType
def updatePD[X : BaseTypedType](
id: Long,
selector: PersonTable => Rep[X],
newValue: X
): DBIO[Int] =
people.filter(_.id === id).map(selector).update(newValue)
What that means is that when you use the method...
updatePD(anId, _.name, "Alice")
...the compiler has to prove to itself that whatever X you use, there is an approproate type representation in Slick.

This is also from Richard, but the exchange took place on gitter.
The only trouble with the first answer is that by demanding an implicit of type BaseTypedType[X] the context bound forces client code for optional columns to provide an implicit of type BaseTypedType[Option[X]] even when BaseTypedType[X] is already available.
This is unnecessary. Slick handles optional columns for you and if you provide an implicit for BaseTypedType[X] you are providing enough for it to handle columns of type Option[X].
So the context bound, while it works, is more demanding than necessary and results in having to write implicits in the client-code that involve directly referencing null and replicating logic already built into Slick. Not good.
The answer is to declare the implicit parameter as a named implicit parameter (called shape below) in its own parameter list, i.e. in long-form, not using the context bound short-hand :BaseTypedType. Then you can specify the more complicated but less demanding constraint used below.
So the solution is:
def updatePD[X] (id: Long, selector: PersonTable => Rep[X], newValue: X)
(implicit shape: Shape[_ <: FlatShapeLevel, Rep[X], X, _]): DBIO[Int] = {
people.filter(_.id === id).map(selector).update(newValue)
}
Understanding why shape has the exact type Shape[_ <: FlatShapeLevel, Rep[X], X, _] depends on an intimate understanding of Slick's types and implicit mechanisms. Richard may yet write a blog post on that!

Related

Scala Implicit syntax in polymorphic methods

I am a Scala noob reading through a parsing library, and have reached some syntax I do not understand:
def parseA[_: P] = P("a")
val Parsed.Success(value, successIndex) = parse("a", parseA(_))
I want to be able to combine these lines into one, ie
val Parsed.Success(value, successIndex) = parse("a", P("a"))
but this gives a compile error:
Error:(8, 61) overloaded method value P with alternatives:
[T](t: fastparse.P[T])(implicit name: sourcecode.Name, implicit ctx: fastparse.P[_])fastparse.P[T] <and>
=> fastparse.ParsingRun.type
cannot be applied to (String)
Error occurred in an application involving default arguments.
val Parsed.Success(value, successIndex) = parse(source, P("a"))
How should this line be written? And can you name the syntax concepts involved to maximise my learning?
_: P is the same as (implicit ctx: P[_]), that means that method is asking for an implicit parameter of type P[_] (the underscore means that it does not care for the inner type. See What are all the uses of an underscore in Scala?).
P("a") is calling this method, which requires such implicit in scope, and that is why in your second example it fails to compile, because it did not find the implicit parameter.
The features sued here are implicits, existential types & macros...
All of them are very advanced techniques. If you are just starting, I would suggest to leave them for latter.
Implicits are very important and useful, I would start from there, but first make sure you feel comfortable with "normal" Scala (whatever that means).
For the second question, I think this should work.
def program[_: P] = parse("a", P("a"))
val Parsed.Success(value, successIndex) = program

value flatMap is not a member of type parameter F[Long] when using cats.effect

This perhaps been asked many times before, but none of the suggestions I've found help.
I have a simple Scala code that generates long number that depends on some side-effects. I'm wrapping thing in an IO monad, but according to the least power principle, I'm actually declaring my function as F[_]: Effect. Now code won't compile and I don't understand why, please suggest what may be wrong
import cats.effect.{Clock, Effect}
import cats.syntax.all._
import java.util.concurrent.TimeUnit
...
def generateId[F[_]: Effect](rid: Long)(implicit F: Effect[F], clock: Clock[F]): F[Long] =
for {
currentTimeNanos <- clock.realTime(TimeUnit.NANOSECONDS)
tid <- F.delay(Thread.currentThread().getId)
} yield
(tid << 40 /* */ & 0xFFFFFF0000000000L) |
(rid << 16 /* */ & 0x000000FFFFFF0000L) |
(currentTimeNanos & 0x000000000000FFFFL)
[error] /.../package.scala:34:41: value flatMap is not a member of type parameter F[Long]
[error] currentTimeNanos <- clock.realTime(TimeUnit.NANOSECONDS)
[error] ^
[error] /.../package.scala:35:34: value map is not a member of type parameter F[Long]
[error] tid <- F.delay(Thread.currentThread().getId)
Also, if you have any suggestions on improving the code, let me know please.
The problem is that the context bound in F[_]: Effect desugars into an implicit parameter, so the compiler is seeing something like this:
def generateId[F[_]](rid: Long)(implicit ev: Effect[F], F: Effect[F], ...): F[Long] = ...
That means that every time it tries to resolve an implicit Effect[F] in the body of the method, it'll fail because it thinks the explicit F and this synthetic ev are ambiguous.
The solution is to drop either the context bound or the explicit implicit F: Effect[F] parameter. I'd suggest killing the context bound, since the fact that Scala allows you to combine the two is part of the reason it's so easy to make this kind of error (and was in my view a serious misjudgement by the language designers, as I've said many times before).

missing parameter type for expanded function for my function; not for another with same signature

Short form: I have a method with the same signature as Future.recover. Passing a partial function to Future's version works. Passing the same PF to my version results in a missing parameter type for expanded function. The argument types of an anonymous function must be fully known. (SLS 8.5) error. What's the difference?
Longer form:
I'm trying to implement the TracingFuture class discussed here in an attempt to trace errors across future boundaries. The basic technique is to wrap the Future in another class, TracingFuture, while adding a pseudo-stacktrace.
The code given in the blog post is missing the recover method from Future, so I've added it with the same signature:
class TracingFuture[+T](underlying: Future[T], val trace: Vector[FutureTraceElement]) extends Future[T] {
def recover[U >: T](pf: PartialFunction[Throwable, U]
)(implicit ec: ExecutionContext, enclosing: sourcecode.Enclosing, file: sourcecode.File,
line: sourcecode.Line): TracingFuture[U] = {
val recovered = underlying.recover(pf)
new TracingFuture[U](recovered, trace :+ FutureTraceElement(enclosing.value, "recover", file.value, line.value))
}
}
For comparison, here's the equivalent chunk of code out of Future. Note that aside from the extra implicit parameters the signatures are the same.
trait Future[+T] extends Awaitable[T] {
def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] =
transform { _ recover pf }
}
Finally, my code that produces the compile error:
val x: TracingFuture[Vector[Maintainer]] = ... // code producing a TracingFuture
val fMaintainers = x.recover {
case err: Throwable ⇒
logger.error("Failed to get list of user maintainers.", err)
Vector.empty[Maintainer]
}
And the error message:
[error] /Users/bwbecker/oat/src/oat3/modules/wapp/app/oat/wapp/dao/CronJobDAO.scala:273: missing parameter type for expanded function
[error] The argument types of an anonymous function must be fully known. (SLS 8.5)
[error] Expected type was: ?
[error] val fMaintainers = x.recover {
[error] ^
Once again, this code works with the Future.recover but I get a compile error with TracingFuture.recover. I don't understand why.
This SO question explains that the compiler knows the argument to the partial function must be a supertype of T but can't guarantee that. But why doesn't it run into that issue with Future.recover?
And, of course, I'd like to know if there's anything I do about it other than rewriting the anonymous partial function to make the types explicit.
The problem is that TracingFuture has two overloaded recover methods: the one you added and the one you inherited from Future. When you only have one, it provides the expected type which is crucial for type inference, but with overloaded methods it doesn't work, as you see from Expected type was: ?.
You may think the compiler should notice types of function parameters are the same and so can still provide the expected type. And you would be right, but it was only fixed in Scala 2.12.
Of course, then you'll run into trouble that the compiler has no way to tell which overload you want when only the implicit arguments are different.
Try to replace
val fMaintainers = x.recover {
case err: Throwable ⇒
logger.error("Failed to get list of user maintainers.", err)
Vector.empty[Maintainer]
}
with
val fMaintainers = x.recover(PartialFunction[Throwable, Vector[Maintainer]] {
case err: Throwable ⇒
logger.error("Failed to get list of user maintainers.", err)
Vector.empty[Maintainer]
})
Why do I get a "missing parameter for expanded function" in one case and not the other?

Slick 3.1.1 ambiguous implicit values involving filter and implicit CanBeQueryCondition

I have been trying to create a Generic Dao over Slick 3.1.1 and it includes a generic filter that competes with JPA's findByExample, see the following files:
GenericDaoImpl.scala Generic level reusable across all Models
UserDao.scala Generic plus customizations for the User model
UserService.scala Wraps the UserDao into more services level functionality
In this last file I try to use the generic filter function to find a user by its registered email, like this:
// this will implicitly exec and wait indefinitely for the
// db.run Future to complete
import dao.ExecHelper._
def findByEmail(email: String): Option[UserRow] = {
userDao.filter(_.email === email).headOption
}
but this produces the compiler error:
[error] /home/bravegag/code/play-authenticate-usage-scala/app/services/UserService.scala:35: value === is not a member of String
[error] userDao.filter(email === _.email).headOption
[error] ^
[error] /home/bravegag/code/play-authenticate-usage-scala/app/services/UserService.scala:35: ambiguous implicit values:
[error] both value BooleanOptionColumnCanBeQueryCondition in object CanBeQueryCondition of type => slick.lifted.CanBeQueryCondition[slick.lifted.Rep[Option[Boolean]]]
[error] and value BooleanCanBeQueryCondition in object CanBeQueryCondition of type => slick.lifted.CanBeQueryCondition[Boolean]
[error] match expected type slick.lifted.CanBeQueryCondition[Nothing]
[error] userDao.filter(email === _.email).headOption
[error] ^
Can anyone advice on how the implicit declaration of the filter function below can be improved to solve this compiler error?
The implementation of the filter function (found in GenericDaoImpl.scala) is:
// T is defined above as T <: Table[E] with IdentifyableTable[PK]
override def filter[C <: Rep[_]](expr: T => C)
(implicit wt: CanBeQueryCondition[C]) : Future[Seq[E]] =
db.run(tableQuery.filter(expr).result)
As far as I can see you are simply lacking you profile API import in UserService.
Just add there this import: import profile.api._ and it should work.
EDIT: BTW I see many people building their own version of base CRUDs for Slick. Did you try some existing thin libraries doing just that e.g. here: https://github.com/VirtusLab/unicorn ? It's not really related to this question but it may be worth to take a look.

slick 2.0 define generic `find by field` method

import scala.slick.driver.MySQLDriver.simple._
class RichTable[T](tag: Tag, name: String) extends Table[T](tag, name) {
case class QueryExt[B](q: Query[RichTable.this.type, B]) {
def whereEq[C](col: RichTable.this.type => Column[C], c: C) = {
q.filter { fields =>
col(fields) === c
}
}
}
}
Then it complains
[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:10: value === is not a member of slick.driver.MySQLDriver.simple.Column[C]
[error] col(fields) === c
[error] ^
[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:9: ambiguous implicit values:
[error] both value BooleanColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Boolean]]
[error] and value BooleanOptionColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Option[Boolean]]]
[error] match expected type scala.slick.lifted.CanBeQueryCondition[Nothing]
[error] q.filter { fields =>
[error] ^
[error] two errors found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed Mar 6, 2014 1:21:48 AM
There have been questions about this, but the answers did not work for 2.0
How to parametrize Scala Slick queries by WHERE clause conditions?
Slick doesn't have any information about C, so it doesn't know if it can and how it should map it to a database value and if it can use === on it. So you get a type error. You will have to use Scala's type system to restrict the type to one for which Slick knows how to map it. You can do this by providing a so-called Context Bound, in this case :BaseColumnType.
def whereEq[C:BaseColumnType](col: RichTable.this.type => Column[C], c: C) = {
q.filter { fields =>
col(fields) === c
}
}
BaseColumnType is provided by Slick and using it in this way basically tells the Scala compiler to look for an implicit value of type BaseColumnType[C] in scope, where you call whereEq. Because then it is usually known what C will actually be. Slick comes with BaseColumnType[Int], BaseColumnType[String], etc. so at the call site, the Scala compiler can find one when your C is really an Int or String in that particular call and this way pass the info further to Slick.
Same for LiuTiger's question. abstract class Crud[..., PK:BaseColumnType] should do the trick, a trait doesn't work with context bounds. When implementing an abstract DAO be prepared to face a lot of challenges and get to the edges of your Scala type system skills and learn quite a bit about type inference order, implicit parameters, etc.