scalaz lens from example does not work - scalaz

When i try to use Lens.lensu from scalaz.7.2.15, (i check http://eed3si9n.com/learning-scalaz/Lens.html)
case class Person(id: Int, name: String)
val idLens: Person #> Int = Lens.lensu(
(p, id) => p.copy(id = id),
_.id
)
val nameLens: Person #> String = Lens.lensu(
(p, name) => p.copy(name = name),
_.name
)
val c = idLens <=< nameLens
But i get error:
found : Person #> String
[error] (which expands to) scalaz.LensFamily[Person,Person,String,String]
[error] required: scalaz.LensFamily[?,?,Person,Person]
[error] val c = idLens <=< nameLens
But it's the same as in example, what is bad with this code?

You can read <=< as "after". Then
idLens <=< nameLens
means: Use idLens after nameLens. For this to work, the "input type" of idLens (which is Person) has to match the "output type" of nameLens. That's why the compiler expects LensFamily[?,?,Person,Person] (that is, output type Person). But the output type of nameLens is String, not Person.
What type do you expect c to have? If you want Person #> (Int, String), then use parallel composition:
val c = idLens *** nameLens

Related

Slick dynamic groupby

I have code like so:
def query(buckets: List[String]): Future[Seq[(List[Option[String]], Option[Double])]] = {
database.run {
groupBy(row => buckets.map(bucket => customBucketer(row.metadata, bucket)))
.map { grouping =>
val bucket = grouping._1
val group = grouping._2
(bucket, group.map(_.value).avg)
}
.result
}
}
private def customBucketer(metadata: Rep[Option[String]], bucket: String): Rep[Option[String]] = {
...
}
I am wanting to be able to create queries in slick which groupby and collect on a given list of columns.
Error I am getting upon compilation is:
[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: List[slick.lifted.Rep[Option[String]]]
[error] Unpacked type: T
[error] Packed type: G
[error] groupBy(row => buckets.map(bucket => customBucketer(row.metadata, bucket)))
[error] ^
Here's a workaround for Slick 3.2.3 (and some background on my approach):
You may have noticed dynamically selecting columns is easy as long as you can assume a fixed type, e.g:
columnNames = List("col1", "col2")
tableQuery.map( r => columnNames.map(name => r.column[String](name)) )
But if you try a similar approach with a groupBy operation, Slick will complain that it "does not know how to map the given types".
So, while this is hardly an elegant solution, you can at least satisfy Slick's type-safety by statically defining both:
groupby column type
Upper/lower bound on the quantity of groupBy columns
A simple way of implementing these two constraints is to again assume a fixed type and to branch the code for all possible quantities of groupBy columns.
Here's the full working Scala REPL session to give you an idea:
import java.io.File
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
import slick.jdbc.H2Profile.api._
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
val confPath = getClass.getResource("/application.conf")
val config = ConfigFactory.parseFile(new File(confPath.getPath)).resolve()
val db = Database.forConfig("slick.db", config)
implicit val system = ActorSystem("testSystem")
implicit val executionContext = system.dispatcher
case class AnyData(a: String, b: String)
case class GroupByFields(a: Option[String], b: Option[String])
class AnyTable(tag: Tag) extends Table[AnyData](tag, "macro"){
def a = column[String]("a")
def b = column[String]("b")
def * = (a, b) <> ((AnyData.apply _).tupled, AnyData.unapply)
}
val table = TableQuery[AnyTable]
def groupByDynamically(groupBys: Seq[String]): DBIO[Seq[GroupByFields]] = {
// ensures columns are returned in the right order
def selectGroups(g: Map[String, Rep[Option[String]]]) = {
(g.getOrElse("a", Rep.None[String]), g.getOrElse("b", Rep.None[String])).mapTo[GroupByFields]
}
val grouped = if (groupBys.lengthCompare(2) == 0) {
table
.groupBy( cols => (cols.column[String](groupBys(0)), cols.column[String](groupBys(1))) )
.map{ case (groups, _) => selectGroups(Map(groupBys(0) -> Rep.Some(groups._1), groupBys(1) -> Rep.Some(groups._2))) }
}
else {
// there should always be at least one group by specified
table
.groupBy(cols => cols.column[String](groupBys.head))
.map{ case (groups, _) => selectGroups(Map(groupBys.head -> Rep.Some(groups))) }
}
grouped.result
}
val actions = for {
_ <- table.schema.create
_ <- table.map(a => (a.column[String]("a"), a.column[String]("b"))) += ("a1", "b1")
_ <- table.map(a => (a.column[String]("a"), a.column[String]("b"))) += ("a2", "b2")
_ <- table.map(a => (a.column[String]("a"), a.column[String]("b"))) += ("a2", "b3")
queryResult <- groupByDynamically(Seq("b", "a"))
} yield queryResult
val result: Future[Seq[GroupByFields]] = db.run(actions.transactionally)
result.foreach(println)
Await.ready(result, Duration.Inf)
Where this gets ugly is when you can have upwards of a few groupBy columns (i.e. having a separate if branch for 10+ cases would get monotonous). Hopefully someone will swoop in and edit this answer for how to hide that boilerplate behind some syntactic sugar or abstraction layer.

Why isn't a member of type Option[String] being serialized?

I have a dummy class Foo which has three members:
import play.api.libs.json.Json
case class Foo(id: String, fooType: FooType, nextId: Option[String])
object Foo {
implicit val fooReads = Json.reads[Foo]
implicit val fooFormat = Json.format[Foo]
}
where FooType is defined as
import play.api.libs.json.Json
case class FooType(a: String, b: String)
object FooType {
implicit val fooTypeReads = Json.reads[FooType]
implicit val fooTypeFormat = Json.format[FooType]
}
I find some interesting behavior when serializing a Foo object. If I serialize a Foo to JSON, parse the JSONified Foo, I find that all the members are parsed correctly:
val id = "id"
val fooType = FooType("a", "b")
val nextId = None
val foo = Foo(id, fooType, nextId)
val jsonFoo = Json.toJson(foo)
val parsedFoo = Json.parse(jsonFoo.toString).as[Foo]
assert(parsedFoo == foo)
assert(parsedFoo.id == id)
assert(parsedFoo.fooType == fooType)
assert(parsedFoo.nextId.isEmpty)
This is good, because that's what I expect.
However, in my next test, I find that the nextId field is not serializable at all:
val id = "id"
val fooType = FooType("a", "b")
val nextId = None
val foo = Foo(id, fooType, nextId)
val jsonFoo = Json.toJson(foo)
assert((jsonFoo \ "id").as[String] == id)
assert((jsonFoo \ "fooType").as[FooType] == fooType)
assert((jsonFoo \ "nextId").as[Option[String]].isEmpty)
This fails with the following error:
Error:(38, 35) No Json deserializer found for type Option[String]. Try to implement an implicit Reads or Format for this type.
assert((jsonFoo \ "nextId").as[Option[String]].isEmpty)
Error:(38, 35) not enough arguments for method as: (implicit fjs: play.api.libs.json.Reads[Option[String]])Option[String].
Unspecified value parameter fjs.
assert((jsonFoo \ "nextId").as[Option[String]].isEmpty)
Similarly, I find that when I print the JSON object dumped by Json.toJson(foo), the nextId field is missing from the JSON object:
println(Json.prettyPrint(jsonFoo))
{
"id" : "id",
"fooType" : {
"a" : "a",
"b" : "b"
}
}
I can, however, parse the nextId field with toOption; i.e.,
assert((jsonFoo \ "nextId").toOption.isEmpty)
How can my object be correctly parsed from JSON if one of its members isn't deserializable natively?
The nextId field is serializable, otherwise, you wouldn't have been able to write a Foo to JSON at all.
jsonFoo: play.api.libs.json.JsValue = {"id":"id","fooType":{"a":"a","b":"b"}}
The problem you're having is that there are no Reads for Option[A]. Options are handled specially by Play JSON. When using JSON combinators, we use readNullable[A] and writeNullable[A] instead of read[Option[A]] and write[Option[A]]. Likewise, when using methods to pull individual fields from a JsValue, calling as will not work because it requires an implicit Reads[A] for the type you give it (in this case a Reads[Option[String]], which does not exist).
Instead, you need to use asOpt, which will correctly handle the Option underneath:
scala> (jsonFoo \ "nextId").asOpt[String]
res1: Option[String] = None
nextId doesn't appear in the printed JSON because the value you are serializing is None. This is what is expected to happen. Since the value is optional, it gets omitted from the JSON (it's just undefined in JavaScript). If it has a value, it will appear:
scala> Json.toJson(Foo("id", FooType("a", "b"), Option("abcdef")))
res3: play.api.libs.json.JsValue = {"id":"id","fooType":{"a":"a","b":"b"},"nextId":"abcdef"}
If something wasn't serializable with Play JSON, it simply wouldn't compile.

How to extract data using Text.JSON?

I'm trying to write a wrapper around ffprobe that extracts value in JSON of the format {"format": {"format_name": value}}. The JSON is output by a created process. Here's what I've gotten to.
import System.Process
import System.Environment
import System.IO
import Text.JSON
main = do
args <- getArgs
(_, Just out, _, p) <- createProcess
(proc "ffprobe" [args!!0, "-of", "json", "-show_format"])
{ std_out = CreatePipe }
s <- hGetContents out
--putStrLn $ show (decode s :: Result JSValue)
--waitForProcess p
--putStrLn $ valFromObj "format_name" format
-- where format = valFromObj "format" rootObj
-- (Ok rootObj) = decode s :: Result (JSObject (JSValue))
let (Ok rootObj) = decode s :: Result (JSObject (JSValue))
let (Ok format) = valFromObj "format" rootObj :: Result (JSObject (JSValue))
putStrLn format_name
where (Ok format_name) = valFromObj "format_name" format
It fails to compile with:
[1 of 1] Compiling Main ( ffprobe.hs, ffprobe.o )
ffprobe.hs:20:59: error:
Variable not in scope: format :: JSObject JSValue
I'm confused about several things, including why I can't get the last line to compile:
Why can't I assert for Ok in the Result after the ::. Like :: Result Ok JSObject JSValue?
Why can't I extract the values in a where clause?
Why is it Result (JSObject (JSValue)) and not Result JSObject JSValue?
Why is format out of scope?
I have a feeling I'm mixing the IO and Result monads together in the same do block or something. Is Result even a monad? Can I extract the value I want in a separate do without crapping all over the IO do?
I think your compile error is because of the position of the where. Try
main = do
...
let (Ok format) = valFromObj "format" rootObj :: Result (JSObject (JSValue))
let (Ok format_name) = valFromObj "format_name" format
putStrLn format_name
The scope of the where is outside the do so it isn't aware of format.
You cannot do this:
main = do
let bar = "only visible inside main? "
return baz
where
baz = bar ++ " yes, this will break!"
This gives:
test.hs:7:11:
Not in scope: ‘bar’
Perhaps you meant ‘baz’ (line 7)
Let bindings unlike function arguments are not available in where bindings. Above bar is not in scope for baz to use it. Compare to your code.

Play 2.3 Scala: Explicitly passing Writer - needs to match Option{T] vs T; implicit writer can handle both

I have a case class with a json.Writes[T] defined on it.
If I have an Option[T], and with an implicit write in scope, I can call Json.toJson(Option[T]); and this works
However if I call Json.toJson(T)(json.Writes[T]) - I get a compile error
type mismatch;
found : play.api.libs.json.Writes[models.WorkflowTemplateMilestone]{def writes(o: models.WorkflowTemplateMilestone): play.api.libs.json.JsObject}
required: play.api.libs.json.Writes[Option[models.WorkflowTemplateMilestone]]
Note: implicit value workflowTemplateMilestoneAPIWrites is not applicable here because it comes after the application point and it lacks an explicit result type
Am I passing the Writer in incorrectly? How does it work between T and Option[T] with the implicit write?
The actual code is below; in case I've mis diagnosed the issue
Custom writer
implicit val workflowTemplateMilestoneAPIWrites = new Writes[WorkflowTemplateMilestone] {
def writes(o: WorkflowTemplateMilestone) = Json.obj(
"id" -> o.id,
"name" -> o.name,
"order" -> o.order
)
}
WORKS
implicit val workflowTemplateMilestoneAPIWrites = new Writes[List[(WorkflowTemplate, Option[WorkflowTemplateMilestone])]] {
def writes(l: List[(WorkflowTemplate, Option[WorkflowTemplateMilestone])]) = Json.obj(
"id" -> l.head._1.id,
"name" -> l.head._1.name,
"milestones" ->
l.map(o =>
Json.toJson(o._2) **// Writer is picked up through Implict this WORKS**
)
)
}
GIVES COMPILE ERROR
implicit val workflowTemplateMilestoneAPIWrites = new Writes[List[(WorkflowTemplate, Option[WorkflowTemplateMilestone])]] {
def writes(l: List[(WorkflowTemplate, Option[WorkflowTemplateMilestone])]) = Json.obj(
"id" -> l.head._1.id,
"name" -> l.head._1.name,
"milestones" ->
l.map(o =>
Json.toJson(o._2)**(WorkflowTemplateMilestone.workflowTemplateMilestoneAPIWrites)** // But if I explicitly pass in the Writer, I get the compile error
)
)
}
Thanks,
Brent

Play Framework 2.3 Scala - Serialize nested objects to JSon with implicit Writes converters

I need for a frontend jquery-component a specific json object like this (ajax response):
[
{"division":"IT", "contacts":[
{“firstname”:”Carl”, “surname”:”Smith”, “empID”:1},
{“firstname”:”Henry”, “surname”:”Miller”, “empID”:2}
]},
{"division":"Sales", "contacts":[
{“firstname”:”Nancy”, “surname”:”McDonald”, “empID”:3},
{“firstname”:”Susan”, “surname”:”McBright”, “empID”:4}
]}
]
In Backend the data is read via anorm (MySQL) and transformed in following object:
List(Map("division" -> "IT", "contacts" -> List(c3,c4)), Map("division" -> "Sales", "contacts" -> List(c3,c4)))
Then I try to serialize the Object to JSon but without success (including implicit Writes converters). Below I made a simplified test case Idea-Worksheet in the same manner:
import play.api.libs.json.{JsValue, Json, Writes}
case class Contact(firstname: String, surname: String, empID: Option[Int])
case class ContactDivisionList(division: String, contacts: Seq[Contact])
implicit val ctWrites = new Writes[Contact] {
def writes(ct: Contact) = Json.obj(
"firstname" -> ct.firstname,
"surname" -> ct.surname,
"empid" -> ct.empID
)
}
implicit val sdlWrites = new Writes[ContactDivisionList] {
def writes(dlist: ContactDivisionList) = Json.obj(
"division" -> dlist.division,
"contacts" -> Json.toJson(dlist.contacts)
)
}
/*
Example
*/
val c1 = Contact("Carl","Smith",Option(1))
val c2 = Contact("Henry","Miller",Option(2))
val c3 = Contact("Nancy","McDonald",Option(3))
val c4 = Contact("Susan","McBright",Option(4))
//Test case 1 ->OK
Json.toJson(List(c1,c2,c3,c4))
//Test case 2 ->OK
val c_comp1=List(Map("contacts" -> List(c1,c2)),Map("contacts" -> List(c3,c4)))
//RESULT --> c_comp1: List[scala.collection.immutable.Map[String,List[Contact]]] = List(Map(contacts -> List(Contact(Carl,Smith,Some(1)), Contact(Henry,Miller,Some(2)))), Map(contacts -> List(Contact(Nancy,McDonald,Some(3)), Contact(Susan,McBright,Some(4)))))
Json.toJson(c_comp1)
//res1: play.api.libs.json.JsValue = [{"contacts": [{"firstname":"Carl","surname":"Smith","empid":1},{"firstname":"Henry","surname":"Miller","empid":2}]},{"contacts":[{"firstname":"Nancy","surname":"McDonald","empid":3},{"firstname":"Susan","surname":"McBright","empid":4}]}]
//Test case 3 ->Fail!!!
val c_comp2 = List(Map("division" -> "IT", "contacts" -> List(c1,c2)),Map("division" -> "Sales", "contacts" -> List(c3,c4)))
//sdlWrites: play.api.libs.json.Writes[ContactDivisionList]{def writes(dlist: ContactDivisionList): play.api.libs.json.JsObject} = $anon$2#3738baec
Json.toJson(c_comp2)
//!!!!!Error messages
/*Error:(39, 13) No Json serializer found for type List[scala.collection.immutable.Map[String,java.io.Serializable]]. Try to implement an implicit Writes or Format for this type.
Json.toJson(c_comp2)
^
Error:(39, 13) not enough arguments for method toJson: (implicit tjs: play.api.libs.json.Writes[List[scala.collection.immutable.Map[String,java.io.Serializable]] ])play.api.libs.json.JsValue.
Unspecified value parameter tjs.
Json.toJson(c_comp2)
^
*/
At the end of the script you can see "Test case 3" that got an error when i execute Json.toJson(c_comp2) -->"No Json serializer found for type..". I try a lot of things but i don't get it right. The only difference to successful "Test case 2" is that i extend the Map with a String-Tuppel.
I hope sombody can help me with that issue, Thx
Best regards
Karsten
Your problem is that the Map you have there is mapping String to the least upper bound of String (your division name) and List[Contact], which happens to be java.io.Serializable.
scala> case class Contact(firstname: String, surname: String, empID: Option[Int])
defined class Contact
scala> case class ContactDivisionList(division: String, contacts: Seq[Contact])
defined class ContactDivisionList
scala> val c1 = Contact("Carl","Smith",Option(1))
c1: Contact = Contact(Carl,Smith,Some(1))
scala> val c2 = Contact("Henry","Miller",Option(2))
c2: Contact = Contact(Henry,Miller,Some(2))
scala> val c3 = Contact("Nancy","McDonald",Option(3))
c3: Contact = Contact(Nancy,McDonald,Some(3))
scala> val c4 = Contact("Susan","McBright",Option(4))
c4: Contact = Contact(Susan,McBright,Some(4))
scala> Map("division" -> "IT", "contacts" -> List(c1,c2))
res10: scala.collection.immutable.Map[String,java.io.Serializable] = Map(division -> IT, contacts -> List(Contact(Carl,Smith,Some(1)), Contact(Henry,Miller,Some(2))))
I'm not entirely sure the nature of your problem, but if you already have List[ContactDivisionList], it's pretty straightforward to serialize that to JSON:
scala> implicit val contactWrites = Json.writes[Contact]
contactWrites: play.api.libs.json.OWrites[Contact] = play.api.libs.json.OWrites$$anon$2#3676af92
scala> implicit val contactDivisionListWrites = Json.writes[ContactDivisionList]
contactDivisionListWrites: play.api.libs.json.OWrites[ContactDivisionList] = play.api.libs.json.OWrites$$anon$2#2999d17d
scala> Json.toJson(List(ContactDivisionList("IT", List(c1,c2)), ContactDivisionList("Sales", List(c3, c4))))
res2: play.api.libs.json.JsValue = [{"division":"IT","contacts":[{"firstname":"Carl","surname":"Smith","empID":1},{"firstname":"Henry","surname":"Miller","empID":2}]},{"division":"Sales","contacts":[{"firstname":"Nancy","surname":"McDonald","empID":3},{"firstname":"Susan","surname":"McBright","empID":4}]}]
It seems to me that you should avoid having that Map in the first place. I've never worked with anorm before, but I think where you need to be looking at is the code that data structure, because at that point, you've lost typesafety. You should ideally work with ContactDivisionList or construct your object using the JsValue cases directly.