Combine Records in Purescript - purescript

Given I have the folowing records in purescript:
let name = {name: "Jim"}
let age = {age: 37}
is it possible to combine those two records some how in a generic way?
Something like:
name 'comb' age
such that I get the following record:
{name: "Jim", age: 37}
Somehow it seems to be possible with the Eff rowtype, but I'm curious if it would be possible with 'normal' records. I'm new to purescript and it's record syntax.
Thanks a lot.

EDIT:
It seems that currently the official package for handling record manipulations is purescript-record - you can find Builder.purs there which provides merge and build functions:
> import Data.Record.Builder (build, merge)
> name = {name: "Jim"}
> age = {age: 37}
> :t (build (merge age) name)
{ name :: String
, age :: Int
}
API NOTE:
This API looks overcomplicated at first glance - especially when you compare it to simple unionMerge name age call (unionMerge is intoduced at the end of this answer). The reason behind Builder existence (and so this API) is performance. I can assure you that this:
> build (merge name >>> merge age) {email: "someone#example.com"}
creates only one new record. But this:
> unionMerge name (unionMerge age {email: "someone#example.com"})
creates two records during execution.
What is even more interesting is how Builder, build and merge are implemented - Builder is newtype wrapper around a function (and its composition is just a function composition) and build is just a function application on copied version of the record:
newtype Builder a b = Builder (a -> b)
build (Builder b) r1 = b (copyRecord r1)
In merge there is unsafeMerge performed:
merge r2 = Builder \r1 -> unsafeMerge r1 r2
So why are we gaining here anything?? Because we can be sure that intermediate results can't escape function scope and that every value is consumed exactly once in builder chain. Therefore we can perform all transformations "in place" in a mutable manner. In other words this intermediate value:
> intermediate = unionMerge name {email: "someone#example.com"}
> unionMerge age intermediate
can't be "extracted" from here:
> build (merge name >>> merge age) {email: "someone#example.com"}
and it is only consumed once by the next builder, namely merge age.
TYPESYSTEM COMMENT:
It seems that Purescript type system can handle this now thanks to the Union type class from Prim:
The Union type class is used to compute the union of two rows
of types (left-biased, including duplicates).
The third type argument represents the union of the first two.
Which has this "magic type" (source: slide 23):
Union r1 r2 r3 | r1 r2 -> r3, r1 r3 -> r2
OLD METHOD (still valid but not preferred):
There is purescript-records package which exposes unionMerge which does exactly what you want (in new psci we don't have to use let):
> import Data.Record (unionMerge)
> name = {name: "Jim"}
> age = {age: 37}
> :t (unionMerge age name)
{ name :: String
, age :: Int
}

note: When this answer was accepted, it was true, but now we do have the row constraints it mentions, and a library for manipulating records that includes merges/unions: https://github.com/purescript/purescript-record
It's not possible to do this at the moment, as we don't have a way of saying that a row lacks some label or other. It is possible to have an open record type:
something :: forall r. { name :: String | r } -> ...
But this only allows us to accept a record with name and any other labels, it doesn't help us out if we want to combine, extend, or subtract from records as it stands.
The issue with combining arbitrary records is we'd have a type signature like this:
comb :: forall r1 r2. { | r1 } -> { | r2 } -> ???
We need some way to say the result (???) is the union of r1 and r2, but also we'd perhaps want to say that r1's labels do not overlap with r2's.
In the future this may be possible via row constraints.

Related

SQL query to include V and E props in the result

I feel like this should be simple, but lots of searching and experimenting has not produced any results.
I have a very simple ODB database with one V and one E class. V has various props and E has one prop: "order"
This simple ODB SQL query...
select expand(out()) from #12:34
...returns all the props from the V records connected by the "out" edges on #12:34 (i.e. the child records of #12:34), working as expected.
But what I'm not able to do is to also include the one prop from those edges, as well as sort by that E prop (which should be trivial once I can get it into the projections).
This works fine in OrientDB v 3.0.0RC2 (the GA will be released in a few days)
MATCH
{class:V, where:(#rid = ?), as:aVertex}.outE(){as:theEdge}.inV(){as:otherVertex}
RETURN theEdge:{*}, otherVertex:{*}
you can also return single properties with
RETURN theEdge.prop1 as p1, theEdge.prop2 as p2, otherVertex.propX as p3

How does scala slick determin which rows to update in this query

I was asked how scala slick determines which rows need to update given this code
def updateFromLegacy(criteria: CertificateGenerationState, fieldA: CertificateGenerationState, fieldB: Option[CertificateNotification]) = {
val a: Query[CertificateStatuses, CertificateStatus, Seq] = CertificateStatuses.table.filter(status => status.certificateState === criteria)
val b: Query[(Column[CertificateGenerationState], Column[Option[CertificateNotification]]), (CertificateGenerationState, Option[CertificateNotification]), Seq] = a.map(statusToUpdate => (statusToUpdate.certificateState, statusToUpdate.notification))
val c: (CertificateGenerationState, Option[CertificateNotification]) = (fieldA, fieldB)
b.update(c)
}
Above code is (as i see it)
a) looking for all rows that have "criteria" for "certificateState"
b) a query for said columns is created
c) a tuple with the values i want to update to is created
then the query is used to find rows where tuple needs to be applied.
Background
I wonder were slick keeps track of the Ids of the rows to update.
What i would like to find out
What is happening behind the covers?
What is Seq in "val a: Query[CertificateStatuses, CertificateStatus, Seq]"
Can someone maybe point out the slick source where the moving parts are located?
OK - I reformatted your code a little bit to easier see it here and divided it into chunks. Let's go through this one by one:
val a: Query[CertificateStatuses, CertificateStatus, Seq] =
CertificateStatuses.table
.filter(status => status.certificateState === criteria)
Above is a query that translated roughly to something along these lines:
SELECT * // Slick would list here all your columns but it's essiantially same thing
FROM certificate_statuses
WHERE certificate_state = $criteria
Below this query is mapped that is, there is a SQL projection applied to it:
val b: Query[
(Column[CertificateGenerationState], Column[Option[CertificateNotification]]),
(CertificateGenerationState, Option[CertificateNotification]),
Seq] = a.map(statusToUpdate =>
(statusToUpdate.certificateState, statusToUpdate.notification))
So instead of * you will have this:
SELECT certificate_status, notification
FROM certificate_statuses
WHERE certificate_state = $criteria
And last part is reusing this constructed query to perform update:
val c: (CertificateGenerationState, Option[CertificateNotification]) =
(fieldA, fieldB)
b.update(c)
Translates to:
UPDATE certificate_statuses
SET certificate_status = $fieldA, notification = $fieldB
WHERE certificate_state = $criteria
I understand that last step may be a little bit less straightforward then others but that's essentially how you do updates with Slick (here - although it's in monadic version).
As for your questions:
What is happening behind the covers?
This is actually outside of my area of expertise. That being said it's relatively straightforward piece of code and I guess that an update transformation may be of some interest. I provided you a link to relevant piece of Slick sources at the end of this answer.
What is Seq in "val a:Query[CertificateStatuses, CertificateStatus, Seq]"
It's collection type. Query specifies 3 type parameters:
mixed type - Slick representation of table (or column - Rep)
unpacked type - type you get after executing query
collection type - collection type were above unpacked types are placed for you as a result of a query.
So to have an example:
CertificateStatuses - this is your Slick table definition
CertificateStatus this is your case class
Seq - this is how your results would be retrieved (it would be Seq[CertificateStatus] basically)
I have it explained here: http://slides.com/pdolega/slick-101#/47 (and 3 next slides or so)
Can someone maybe point out the slick source where the moving parts are located?
I think this part may be of interest - it shows how query is converted in update statement: https://github.com/slick/slick/blob/51e14f2756ed29b8c92a24b0ae24f2acd0b85c6f/slick/src/main/scala/slick/jdbc/JdbcActionComponent.scala#L320
It may be also worth to emphasize this:
I wonder were slick keeps track of the Ids of the rows to update.
It doesn't. Look at generated SQLs. You may see them by adding following configuration to your logging (but you also have them in this answer):
<logger name="slick.jdbc.JdbcBackend.statement" level="DEBUG" />
(I assumed logback above).

Scala: How to create a map over a collection from a set of keys?

Say I have a set of people Set[People]. Each person has an age. I want to create a function, which creates a Map[Int, Seq[People]] where for each age from, say, 0 to 100, there would be a sequence of people of that age or an empty sequence if there were no people of that age in the original collection.
I.e. I'm doing something along the lines
Set[People].groupBy(_.age)
where the output was
Map[Int, Seq[People]](0 -> Seq[John,Mary], 1-> Seq[People](), 2 -> Seq[People](Bill)...
groupBy of course omits all those ages for which there are no people. How should I implement this?
Configure a default value for your map:
val grouped = people.groupBy(_.age).withDefaultValue(Set())
if you need the values to be sequences you can map them
val grouped = people.groupBy(_.age).mapValues(_.toSeq).withDefaultValue(Seq())
Remember than, as the documentation puts it:
Note: `get`, `contains`, `iterator`, `keys`, etc are not affected by `withDefault`.
Since you've got map with not empty sequences corresponding to ages, you can fill the rest with empty collections:
val fullMap = (0 to 100).map (index => index -> map.getOrElse(index, None)).toMap

FlinkML: Joining DataSets of LabeledVector does not work

I am currently trying to join two DataSets (part of the flink 0.10-SNAPSHOT API). Both DataSets have the same form:
predictions:
6.932018685453303E155 DenseVector(0.0, 1.4, 1437.0)
org:
2.0 DenseVector(0.0, 1.4, 1437.0)
general form:
LabeledVector(Double, DenseVector(Double,Double,Double))
What I want to create is a new DataSet[(Double,Double)] containing only the labels of the two DataSets i.e.:
join:
6.932018685453303E155 2.0
Therefore I tried the following command:
val join = org.join(predictions).where(0).equalTo(0){
(l, r) => (l.label, r.label)
}
But as a result 'join' is empty. Am I missing something?
You are joining on the label field (index 0) of the LabeledVector type, i.e., building all pairs of elements with matching labels. Your example indicates that you want to join on the vector field instead.
However, joining on the vector field, for example by calling:
org.join(predictions).where("vector").equalTo("vector"){
(l, r) => (l.label, r.label)
}
will not work, because DenseVector, the type of the vector field, is not recognized as key type by Flink (such as all kinds of Arrays).
Till describes how to compare prediction and label values in a comment below.

Use forall instead of filter on List[A]

Am trying to determine whether or not to display an overtime game display flag in weekly game results report.
Database game results table has 3 columns (p4,p5,p6) that represent potential overtime game period score total ( for OT, Double OT, and Triple OT respectively). These columns are mapped to Option[Int] in application layer.
Currently I am filtering through game result teamA, teamB pairs, but really I just want to know if an OT game exists of any kind (vs. stepping through the collection).
def overtimeDisplay(a: GameResult, b: GameResult) = {
val isOT = !(List(a,b).filter(_.p4.isDefined).filter(_.p5.isDefined).filter(_.p6.isDefined).isEmpty)
if(isOT) {
<b class="b red">
{List( ((a.p4,a.p5,a.p6),(b.p4,b.p5,b.p6)) ).zipWithIndex.map{
case( ((Some(_),None,None), (Some(_),None,None)), i)=> "OT"
case( ((Some(_),Some(_),None), (Some(_),Some(_),None )), i)=> "Double OT"
case( ((Some(_),Some(_),Some(_)), (Some(_),Some(_),Some(_) )), i)=> "Triple OT"
}}
</b>
}
else scala.xml.NodeSeq.Empty
}
Secondarily, the determination of which type of overtime to display, currently that busy pattern match (which, looking at it now, does not appear cover all the scoring scenarios), could probably be done in a more functional/concise manner.
Feel free to lay it down if you have the better way.
Thanks
Not sure if I understand the initial code correctly, but here is an idea:
val results = List(a, b).map(r => Seq(r.p4, r.p5, r.p6).flatten)
val isOT = results.exists(_.nonEmpty)
val labels = IndexedSeq("", "Double ", "Triple ")
results.map(p => labels(p.size - 1) + "OT")
Turning score column to flat list in first line is crucial here. You have GameResult(p4: Option[Int], p5: Option[Int], p6: Option[Int]) which you can map to Seq[Option[Int]]: r => Seq(r.p4, r.p5, r.p6) and later flatten to turn Some[Int] to Int and get rid of None. This will turn Some(42), None, None into Seq(42).
Looking at this:
val isOT = !(List(a,b).filter(_.p4.isDefined).filter(_.p5.isDefined).filter(_.p6.isDefined).isEmpty)
This can be rewritten using exists instead of filter. I would rewrite it as follows:
List(a, b).exists(x => x.p4.isDefined && x.p5.isDefined && x.p6.isDefined)
In addition to using exists, I am combining the three conditions you passed to the filters into a single anonymous function.
In addition, I don't know why you're using zipWithIndex when it doesn't seem as though you're using the index in the map function afterwards. It could be removed entirely.