How to have row without particular field as function param - purescript

What I want is following, I have a row type:
type FooBarBaz =
( foo :: Int
, bar :: String
, baz :: Boolean
)
Then a need a function that takes this row type record but without "foo": and adds default "foo" field:
withoutFoo { bar: "bar", baz: true} -- > { foo: 1, bar: "bar", baz: true}
I tried to do something like this, but can not get it around:
withoutFoo :: ∀ r.
Lacks "foo" r =>
Cons "foo" Int r FooBarBaz =>
{ | r } -> { | FooBarBaz }
withoutFoo props = Record.union { foo: 1 } props
UPD:
managed to solve it with Union constraint:
withoutFoo :: ∀ r.
Union r ( foo :: Int ) FooBarBaz =>
{ | r } -> { | FooBarBaz }
withoutFoo props =
Record.merge props { foo: 1 }

The problem is that your function isn't actually generic. There is only one possible choice for r: it should always be (bar :: String, baz :: Boolean)
So just make that the type of your function:
withoutFoo :: { bar :: String, baz :: Boolean } -> { | FooBarBaz }
withoutFoo props = Record.union { foo: 1 } props

Related

Extending existing Record type

If I have a record type:
type Rec_ab = { a :: String, b :: Int }
Can I get from it type which is effectfully extended with {c :: Boolean} : { a :: String, b :: Int, c :: Boolean }?
If I had a row, I could do it:
Row_ab = ( a :: String, b :: Int )
Rec_abc = { c :: Boolean | Row_ab }
but if I have a record Rec_ab how this should be solved? I'm not aware of the way of going from Record -> Row.
In general, you can't.
If you need that kind of extensibility, your approach with Row_ab is good enough, but another possible solution is to parametrize the record and then use the parameter to extend it:
type Rec_ab r = { a :: String, b :: Int | r }
type Rec_abc = Rec_ab ( c :: Boolean )
This also allows you to write functions that would work with both Rec_ab and Rec_abc:
add42 :: forall r. Rec_ab r -> Rec_ab r
add42 r = r { b = r.b + 42 }

Slick Query Enrichment Table Column

I'm having an issue with Slick 3 getting a proper reference to the Table in an implicit query enhancement. This code works fine in Slick 2 but the new Table only has a Seq[Columns] on which I still can't call the column method on.
class SlickUtils {
implicit class QueryEnrichment[M,U<: Table[U] /*not valid */,C[_]](q: Query[U,M,C]) {
def sortDynamic(sortString: String): Query[U,M,C] = {
val sortKeys = sortString.split(',').toList.map(_.split('.').map(_.toUpperCase).toList)
sortDynamicImpl(sortKeys)
}
private def sortDynamicImpl(sortKeys: List[Seq[String]]): Query[U,M,C] = {
sortKeys match {
case key :: tail =>
sortDynamicImpl(tail).sortBy(table =>
key match {
case name :: Nil => table.column[String](name).desc // DOES NOT HAVE COLUMN METHOD
}
)
}
}
}
}
We use this trait with slick 3 and it works like a charm
trait Sorting {
implicit class QueryExtensions3[T <: Table[_], E, C[_]](val query: Query[T, E, C]) {
def sortDynamic(sortString: String): Query[T, E, C] = {
// split string into useful pieces
val sortKeys = sortString.split(',').toList.map(_.split('.').toList)
sortDynamicImpl(sortKeys)
}
private[this] def sortDynamicImpl(sortKeys: List[Seq[String]]): Query[T, E, C] = {
sortKeys match {
case key :: tail =>
sortDynamicImpl(tail).sortBy(table =>
key match {
case name :: Nil => table.column[String](name).asc
case name :: "asc" :: Nil => table.column[String](name).asc
case name :: "desc" :: Nil => table.column[String](name).desc
case o => throw new Exception("invalid sorting key: " + o)
})
case Nil => query
}
}
}
}

scala type mismatch. found type, required _$1

I'm getting a compiler error that I don't understand. The code below produces the following error:
error: type mismatch;
found : oldEntity.type (with underlying type com.mycompany.address.AddressEntity)
required: $1
this.esDocsForAddressEntity.filter(.shouldTriggerRefresh(oldEntity, newEntity)).map(_.esDocName)
object AddressToEsDocMapper {
private val esDocsForAddressEntity = List[EsDocRefreshTrigger[_]](new PartyAddressRefreshTrigger())
def findEsDocsForUpdate(oldEntity : AddressEntity, newEntity : AddressEntity) : List[String] = {
this.esDocsForAddressEntity.filter(_.shouldTriggerRefresh(oldEntity, newEntity)).map(_.esDocName)
}
private class PartyAddressRefreshTrigger extends EsDocRefreshTrigger[AddressEntity] {
val esDocName = "PartyAddress"
override def shouldTriggerRefresh(oldEntity : AddressEntity, newEntity : AddressEntity) : Boolean = {
oldEntity.addressLine2 != newEntity.addressLine2 ||
oldEntity.addressLine3 != newEntity.addressLine3 ||
oldEntity.addressLine1 != newEntity.addressLine1
}
}
}
You don't provide all the code, but presumably it's because you're using a wildcard in the definition of esDocsForAddressEntity. In solving for type params in the expression, it has no way to correlate oldEntity with the type arg of an arbitrary EsDocRefreshTrigger.
The $1 names are just internal or temporary names for the compiler.
Not a great example, but:
scala> val ss = List[Option[_]](Some("a"))
ss: List[Option[_]] = List(Some(a))
scala> ss filter (_.isDefined)
res2: List[Option[_]] = List(Some(a))
scala> ss filter (_.get.length > 0)
<console>:9: error: value length is not a member of _$1
ss filter (_.get.length > 0)
^
Brute force:
scala> class Foo { type A ; def foo(a: A) = 42 }
defined class Foo
scala> class Bar extends Foo { type A = Int }
defined class Bar
scala> class Baz extends Foo { type A = String }
defined class Baz
scala> val foos = List[Foo](new Bar, new Baz)
foos: List[Foo] = List(Bar#55cb6996, Baz#1807e3f6)
scala> foos map { case bar: Bar => bar foo 3 ; case baz: Baz => baz foo "three" }
res1: List[Int] = List(42, 42)
scala> def f(x: Foo)(a: x.A) = x foo a
f: (x: Foo)(a: x.A)Int
Or typeclass that supplies Bars and Bazes with foo methods.

Infix opertors in constructor to set fields

I want to define one (or two, depends on how you look at it) operators in scala.
Something like this : _ ==> _ | _ where the underscore stands for the arguments.
The tricky part is, that the operator should be used in an constructor to set the fields of the object.
I wrote a small example, using implicits and a wrapper object. But that approach clearly does not work.
I don't know, how i can set the fields of the current object.
In this example, every create object from type B should have set its fields to "B", 44 and "foo". The result of "B" ==> 44 | "foo" should be the same as this.a = "B"; this.b = 44; this.c = foo.
Is there a way to achieve that?
Thanks!
abstract class Top {
var a = "a"
var b = 3
var c = "c"
implicit def js2WJ(js: String) : Wrapper = {
new Wrapper(js)
}
}
case class B() extends Top
{
"B" ==> 44 | "foo"
}
class Wrapper(js: String)
{
var ju : Option[Int] = None
var cs : Option[String] = None
def ==>(j: Int): Wrapper = {
ju = Some(j)
this
}
def | (cs: String) =
{
this.cs = Some(cs)
}
}
Is this what you needed?
abstract class Top {
var a = "a"
var b = 3
var c = "c"
implicit def js2WJ(js: String): TopBuilder = TopBuilder(js, b, c, this)
}
case class TopBuilder(a: String, b: Int, c: String, top: Top) {
def ==> (j: Int) = copy(b = j)
def | (s: String) = copy(c = s) toTop
def toTop {
top.a = a
top.b = b
top.c = c
}
}
object B extends Top {
"B" ==> 44 | "foo"
}

scala Either.RightProjection confusion (for comprehension de-sugaring)

I can use an = in a scala for-comprehension (as specified in section 6.19 of the SLS) as follows:
Option
Suppose I have some function String => Option[Int]:
scala> def intOpt(s: String) = try { Some(s.toInt) } catch { case _ => None }
intOpt: (s: String)Option[Int]
Then I can use it thus
scala> for {
| str <- Option("1")
| i <- intOpt(str)
| val j = i + 10 //Note use of = in generator
| }
| yield j
res18: Option[Int] = Some(11)
It was my understanding that this was essentially equivalent to:
scala> Option("1") flatMap { str => intOpt(str) } map { i => i + 10 } map { j => j }
res19: Option[Int] = Some(11)
That is, the embedded generator was a way of injecting a map into a sequence of flatMap calls. So far so good.
Either.RightProjection
What I actually want to do: use a similar for-comprehension as the previous example using the Either monad.
However, if we use it in a similar chain, but this time using the Either.RightProjection monad/functor, it doesn't work:
scala> def intEither(s: String): Either[Throwable, Int] =
| try { Right(s.toInt) } catch { case x => Left(x) }
intEither: (s: String)Either[Throwable,Int]
Then use:
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str).right //note the "right" projection is used
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Product with Serializable with Either[java.lang.Throwable,(Int, Int)]
i <- intEither(str).right
^
The issue has something to do with the function that a right-projection expects as an argument to its flatMap method (i.e. it expects an R => Either[L, R]). But modifying to not call right on the second generator, it still won't compile.
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str) // no "right" projection
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Either[Throwable,Int]
i <- intEither(str)
^
Mega-Confusion
But now I get doubly confused. The following works just fine:
scala> for {
| x <- Right[Throwable, String]("1").right
| y <- Right[Throwable, String](x).right //note the "right" here
| } yield y.toInt
res39: Either[Throwable,Int] = Right(1)
But this does not:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
<console>:14: error: type mismatch;
found : Either.RightProjection[Throwable,String]
required: Either[?,?]
Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
^
I thought these were equivalent
What is going on?
How can I embed an = generator in a for comprehension across an Either?
The fact that you cannot embed the = in the for-comprehension is related to this issue reported by Jason Zaugg; the solution is to Right-bias Either (or create a new data type isomorphic to it).
For your mega-confusion, you expanded the for sugar incorrectly. The desugaring of
for {
b <- x(a)
c <- y(b)
} yield z(c)
is
x(a) flatMap { b =>
y(b) map { c =>
z(c) }}
and not
x(a) flatMap { b => y(b)} map { c => z(c) }
Hence you should have done this:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right map { y => y.toInt } }
res49: Either[Throwable,Int] = Right(1)
More fun about desugaring (the `j = i + 10` issue)
for {
b <- x(a)
c <- y(b)
x1 = f1(b)
x2 = f2(b, x1)
...
xn = fn(.....)
d <- z(c, xn)
} yield w(d)
is desugared into
x(a) flatMap { b =>
y(b) map { c =>
x1 = ..
...
xn = ..
(c, x1, .., xn)
} flatMap { (_c1, _x1, .., _xn) =>
z(_c1, _xn) map w }}
So in your case, y(b) has result type Either which doesn't have map defined.