The trait below compiles fine without the #specialized annotations, or without the map method. Otherwise, it will fail with a compilation error, which doesn't make a lot of sense (at least to me):
[error] (compile:compile) scala.tools.nsc.symtab.Types$TypeError: type mismatch;
[error] found : U(in method foreach)(in method foreach)
[error] required: U(in method foreach)(in method foreach)
This is the trait that I'm talking about:
trait Tuple2Traversable[#specialized(Int, Byte) +A, #specialized(Int, Byte) +B] {
def foreach[T](fn: (A, B) => T)
def map[T](fn: (A, B) => T): Traversable[T] = new Traversable[T] {
def foreach[U](f: T => U) {
def composed(a: A, b: B) = f(fn(a, b))
Tuple2Traversable.this.foreach(composed)
}
}
def flatMap[T](fn: (A, B) => Traversable[T]): Traversable[T] = new Traversable[T] {
def foreach[U](f: (T) => U) {
def composed(a: A, b: B) = fn(a, b).foreach(f)
Tuple2Traversable.this.foreach(composed)
}
}
def filter(included: (A, B) => Boolean): Tuple2Traversable[A, B] = new Tuple2Traversable[A, B] {
def foreach[T](fn: (A, B) => T) {
def composed(a: A, b: B) = if (included(a, b)) fn(a, b)
Tuple2Traversable.this.foreach(composed)
}
}
def foldLeft[T](z: T)(fn: (T, A, B) => T): T = {
var current = z
def op(a: A, b: B) {
current = fn(current, a, b)
}
foreach(op)
current
}
def asTraversable = new Traversable[(A, B)] {
def foreach[U](f: ((A, B)) => U) {
def tupled(a: A, b: B) = f((a, b))
Tuple2Traversable.this.foreach(tupled)
}
}
}
I've been staring at this for a while now. Any suggestions on how to solve this would be highly appreciated.
Perhaps I should add that the purpose of this class is to have a Traversable of tuples, without forcing those tuples to ever be created. A Traversable[(A,B)] would accept a Tuple2[A,B] => T as the parameter of foreach. I want my 'traversable' to accept a function (A, B) => T. (Like def fn(a: Int, b: Int) = a + b.)
Looks like some internal compiler bug. I get the same error in scala 2.9.2, but it compiles fine in scala 2.10-RC2
Related
I'd like to migrate the following code from Scala 2.12 to 2.13
Given any collection of tuples Coll[(A, B)] and a method f: B => IterableOnce[C], I'd like to produce a Coll[(A, C)] by applying f on the second element of the tuple.
implicit class TuplesOps[A, B, Repr <: Traversable[(A, B)]](val s: TraversableLike[(A, B), Repr]) extends AnyVal {
def flatMapValues[C, That](f: B => TraversableOnce[C])(implicit bf: CanBuildFrom[Repr, (A, C), That]) =
s.flatMap { case (a, b) => f(b).map((a, _))}
}
I know that the collection API has changed in 2.13 and I read : https://docs.scala-lang.org/overviews/core/custom-collection-operations.html
I've tried 2 implementations
First
import scala.collection.{ AbstractView, BuildFrom }
import scala.collection.generic.IsSeq
object Evidence extends App {
class TuplesOps[Repr, S <: IsSeq[Repr]](coll: Repr, seq: S) {
def flatMapValues[B, C, D, That](f: C => IterableOnce[D])(implicit bf: BuildFrom[Repr, (B, D), That], ev: seq.A =:= (B, C)): That = {
val seqOps = seq(coll)
bf.fromSpecific(coll)(new AbstractView[(B, D)] {
override def iterator: Iterator[(B, D)] = {
seqOps.flatMap { x => f(ev(x)._2).map((ev(x)._1, _))}.iterator
}
})
}
}
implicit def TuplesOps[Repr](coll: Repr)(implicit seq: IsSeq[Repr]): TuplesOps[Repr, seq.type] =
new TuplesOps(coll, seq)
List("a"->1, "b"->2).flatMapValues{(x:Int) => Seq.fill(x)("x")}
}
And I have this compilation error :
[error] /home/yamo/projects/perso/coll213/src/main/scala/example/Evidence.scala:22:37: diverging implicit expansion for type scala.collection.BuildFrom[List[(String, Int)],(B, String),That]
[error] starting with method Tuple9 in object Ordering
[error] List("a"->1, "b"->2).flatMapValues{(x:Int) => Seq.fill(x)("x")}
Second
import scala.collection.{ AbstractView, BuildFrom }
import scala.collection.generic.IsSeq
object Aux extends App {
type Aux[Repr, B, C] = IsSeq[Repr] { type A = (B, C)}
class TuplesOps[Repr, B, C, S <: Aux[Repr, B, C]](coll: Repr, seq: S) {
def flatMapValues[D, That](f: C => IterableOnce[D])(implicit bf: BuildFrom[Repr, (B, D), That]): That = {
val seqOps = seq(coll)
bf.fromSpecific(coll)(new AbstractView[(B, D)] {
// same as before
override def iterator: Iterator[(B, D)] = {
seqOps.flatMap { case (b, c) => f(c).map((b, _))}.iterator
}
})
}
}
implicit def TuplesOps[Repr, B, C](coll: Repr)(implicit seq: Aux[Repr, B, C]): TuplesOps[Repr, B, C, seq.type] =
new TuplesOps(coll, seq)
List("a"->1, "b"->2).flatMapValues(Seq.fill(_)("x"))
}
And I have this compilation error
[error] /home/yamo/projects/perso/coll213/src/main/scala/example/Aux.scala:24:24: value flatMapValues is not a member of List[(String, Int)]
[error] List("a"->1, "b"->2).flatMapValues(Seq.fill(_)("x"))
Could you help me to make both solution work, if possible ?
I agree, the Custom Collection instructions aren't what they should be.
I find the Factory method a bit easier than the BuildFrom, but I'm still getting used to either/both of them.
import scala.collection.Factory
implicit
class TuplesOps[A,B,CC[x] <: Iterable[x]](val ts: CC[(A,B)]) {
def flatMapValues[C](f: B => IterableOnce[C]
)(implicit fac: Factory[(A,C), CC[(A,C)]]
): CC[(A,C)] =
ts.flatMap{case (a,b) => f(b).iterator.map(x => (a,x))}
.to[CC[(A,C)]](fac)
}
This passes all my simple (minded) tests. It won't work on an Array or an Iterator but then your original Scala 2.12.x version didn't either (for me) so I figured that was okay.
I would like to "compose" two monoids using cats. If there exists a defined Monoid[(A, A) => Int], then I would like to be able to create a Monoid[Preference[A]] using the combine and empty methods of the Monoid[(A, A) => Int]. I am using the term "composing" loosely here because I am not sure that the transform I want to do is accurately called composition.
Here is my current attempt...
import cats._
import cats.implicits._
trait Preference[A] extends Order[A]
object Preference {
def from[A](f: (A, A) => Int): Preference[A] = {
new Preference[A] {
def compare(a1: A, a2: A): Int = {
f(a1, a2)
}
}
}
def monoid[A](implicit ev: Monoid[(A, A) => Int]): Monoid[Preference[A]] = {
new Monoid[Preference[A]] {
def combine(p1: Preference[A], p2: Preference[A]): Preference[A] = {
new Preference[A] {
def compare(a1: A, a2:A): Int = {
ev.combine(p1.compare, p2.compare)(a1, a2)
}
}
}
def empty: Preference[A] = {
from(ev.empty)
}
}
}
}
...this compiles but I would like to know if there is a more idiomatic solution available using cats.
Seems like it should be possible to somehow compose the Monoid[(A,A) => Int] with the from combinator that takes a f:(A, A) => Int and returns a Preference[A] to create a Monoid[Preference[A]] but I can not figure out how to do it.
I have seen this SO post which discusses composing monoids using a product combinator which is not what I want.
I'm not aware of anything built-in into cats directly.
It seems that you have an isomorphism between Preference[A] and (A, A) => Int, and you simply want to transfer the monoid-structure from (A, A) => Int to Preference[A]. This can be expressed generically for arbitrary types A and B:
def fromIsomorphicMonoid[A, B](
forward: A => B,
inverse: B => A
)(implicit aMon: Monoid[A]): Monoid[B] = new Monoid[B] {
def combine(b1: B, b2: B): B =
forward(aMon.combine(inverse(b1), inverse(b2)))
def empty: B = forward(aMon.empty)
}
With this helper method, your monoid in Preference becomes just:
def monoid[A](implicit ev: Monoid[(A, A) => Int]): Monoid[Preference[A]] =
fromIsomorphicMonoid(
from,
(p: Preference[A]) => (x:A, y:A) => p.compare(x, y)
)
Full compilable example (without any dependencies):
trait Monoid[X] {
def empty: X
def combine(x: X, y: X): X
}
trait Order[A] {
def compare(a1: A, a2: A): Int
}
def fromIsomorphicMonoid[A, B](
forward: A => B,
inverse: B => A
)(implicit aMon: Monoid[A]): Monoid[B] = new Monoid[B] {
def combine(b1: B, b2: B): B =
forward(aMon.combine(inverse(b1), inverse(b2)))
def empty: B = forward(aMon.empty)
}
trait Preference[A] extends Order[A]
object Preference {
def from[A](f: (A, A) => Int): Preference[A] = {
new Preference[A] {
def compare(a1: A, a2: A): Int = {
f(a1, a2)
}
}
}
def monoid[A](implicit ev: Monoid[(A, A) => Int])
: Monoid[Preference[A]] = fromIsomorphicMonoid(
from,
(p: Preference[A]) => (x:A, y:A) => p.compare(x, y)
)
}
When I learn State Monad, I'm not sure how to compose two functions with different State return types.
State Monad definition:
case class State[S, A](runState: S => (S, A)) {
def flatMap[B](f: A => State[S, B]): State[S, B] = {
State(s => {
val (s1, a) = runState(s)
val (s2, b) = f(a).runState(s1)
(s2, b)
})
}
def map[B](f: A => B): State[S, B] = {
flatMap(a => {
State(s => (s, f(a)))
})
}
}
Two different State types:
type AppendBang[A] = State[Int, A]
type AddOne[A] = State[String, A]
Two methods with differnt State return types:
def addOne(n: Int): AddOne[Int] = State(s => (s + ".", n + 1))
def appendBang(str: String): AppendBang[String] = State(s => (s + 1, str + " !!!"))
Define a function to use the two functions above:
def myAction(n: Int) = for {
a <- addOne(n)
b <- appendBang(a.toString)
} yield (a, b)
And I hope to use it like this:
println(myAction(1))
The problem is myAction is not compilable, it reports some error like this:
Error:(14, 7) type mismatch;
found : state_monad.State[Int,(Int, String)]
required: state_monad.State[String,?]
b <- appendBang(a.toString)
^
How can I fix it? Do I have to define some Monad transformers?
Update: The question may be not clear, let me give an example
Say I want to define another function, which uses addOne and appendBang internally. Since they all need existing states, I have to pass some to it:
def myAction(n: Int)(addOneState: String, appendBangState: Int): ((String, Int), String) = {
val (addOneState2, n2) = addOne(n).runState(addOneState)
val (appendBangState2, n3) = appendBang(n2.toString).runState(appendBangState)
((addOneState2, appendBangState2), n3)
}
I have to run addOne and appendBang one by one, passing and getting the states and result manually.
Although I found it can return another State, the code is not improved much:
def myAction(n: Int): State[(String, Int), String] = State {
case (addOneState: String, appendBangState: Int) =>
val (addOneState2, n2) = addOne(n).runState(addOneState)
val (appendBangState2, n3) = appendBang(n2.toString).runState( appendBangState)
((addOneState2, appendBangState2), n3)
}
Since I'm not quite familiar with them, just wondering is there any way to improve it. The best hope is that I can use for comprehension, but not sure if that's possible
Like I mentioned in my first comment, it will be impossible to use a for comprehension to do what you want, because it can not change the type of the state (S).
Remember that a for comprehension can be translated to a combination of flatMaps, withFilter and one map. If we look at your State.flatMap, it takes a function f to change a State[S,A] into State[S, B]. We can use flatMap and map (and thus a for comprehension) to chain together operations on the same state, but we can't change the type of the state in this chain.
We could generalize your last definition of myAction to combine, compose, ... two functions using state of a different type. We can try to implement this generalized compose method directly in our State class (although this is probably so specific, it probably doesn't belong in State). If we look at State.flatMap and myAction we can see some similarities:
We first call runState on our existing State instance.
We then call runState again
In myAction we first use the result n2 to create a State[Int, String] (AppendBang[String] or State[S2, B]) using the second function (appendBang or f) on which we then call runState. But our result n2 is of type String (A) and our function appendBang needs an Int (B) so we need a function to convert A into B.
case class State[S, A](runState: S => (S, A)) {
// flatMap and map
def compose[B, S2](f: B => State[S2, B], convert: A => B) : State[(S, S2), B] =
State( ((s: S, s2: S2) => {
val (sNext, a) = runState(s)
val (s2Next, b) = f(convert(a)).runState(s2)
((sNext, s2Next), b)
}).tupled)
}
You then could define myAction as :
def myAction(i: Int) = addOne(i).compose(appendBang, _.toString)
val twoStates = myAction(1)
// State[(String, Int),String] = State(<function1>)
twoStates.runState(("", 1))
// ((String, Int), String) = ((.,2),2 !!!)
If you don't want this function in your State class you can create it as an external function :
def combineStateFunctions[S1, S2, A, B](
a: A => State[S1, A],
b: B => State[S2, B],
convert: A => B
)(input: A): State[(S1, S2), B] = State(
((s1: S1, s2: S2) => {
val (s1Next, temp) = a(input).runState(s1)
val (s2Next, result) = b(convert(temp)).runState(s2)
((s1Next, s2Next), result)
}).tupled
)
def myAction(i: Int) =
combineStateFunctions(addOne, appendBang, (_: Int).toString)(i)
Edit : Bergi's idea to create two functions to lift a State[A, X] or a State[B, X] into a State[(A, B), X].
object State {
def onFirst[A, B, X](s: State[A, X]): State[(A, B), X] = {
val runState = (a: A, b: B) => {
val (nextA, x) = s.runState(a)
((nextA, b), x)
}
State(runState.tupled)
}
def onSecond[A, B, X](s: State[B, X]): State[(A, B), X] = {
val runState = (a: A, b: B) => {
val (nextB, x) = s.runState(b)
((a, nextB), x)
}
State(runState.tupled)
}
}
This way you can use a for comprehension, since the type of the state stays the same ((A, B)).
def myAction(i: Int) = for {
x <- State.onFirst(addOne(i))
y <- State.onSecond(appendBang(x.toString))
} yield y
myAction(1).runState(("", 1))
// ((String, Int), String) = ((.,2),2 !!!)
I would like to convert an expression such as: a.meth(b) to a function of type (A, B) => C that performs that exact computation.
My best attempt so far was along these lines:
def polish[A, B, C](symb: String): (A, B) => C = { (a, b) =>
// reflectively check if "symb" is a method defined on a
// if so, reflectively call symb, passing b
}
And then use it like this:
def flip[A, B, C](f : (A, B) => C): (B, A) => C = {(b, a) => f(a,b)}
val op = flip(polish("::"))
def reverse[A](l: List[A]): List[A] = l reduceLeft op
As you can pretty much see, it is quite ugly and you have to do a lot of type checking "manually".
Is there an alternative ?
You can achieve it easily with plain old subtype polymorphism. Just declare interface
trait Iface[B, C] {
def meth(b: B): C
}
Then you could implement polish easily
def polish[B, C](f: (Iface[B, C], B) => C): (Iface[B, C], B) => C = { (a, b) =>
f(a, b)
}
Using it is completely typesafe
object IfaceImpl extends Iface[String, String] {
override def meth(b: String): String = b.reverse
}
polish((a: Iface[String, String], b: String) => a meth b)(IfaceImpl, "hello")
Update:
Actually, you could achieve it using closures only
def polish[A, B, C](f: (A, B) => C): (A, B) => C = f
class Foo {
def meth(b: String): String = b.reverse
}
polish((_: Foo) meth (_: String))(new Foo, "hello")
Or without helper function at all :)
val polish = identity _ // Magic at work
((_: Foo) meth (_: String))(new Foo, "hello")
I saw the below code on SO. But the problem it has is that it's map and flatMap definitions aren't fully correct. map must be able to transform the inside value of a monadic container/context, and the same way a flatMap should take a value, transform it and put it back in the monadic context/container. In the example below it's taking an A and giving an A or M[A] (instead of A->B or A->M[B])
trait Mine[A] {
def get(): A
def map(f: A => A): Mine[A]
def flatMap(f: A => Mine[A]): Mine[A]
}
case class Attempt1[A, B](a: (A, B)) extends Mine[(A, B)] {
def get(): (A, B) = a
def map(f: ((A, B)) => (A, B)): Mine[(A, B)] = {
Attempt1(f(a._1, a._2))
}
def flatMap(f: ((A, B)) => Mine[(A, B)]): Mine[(A, B)] = {
f(a._1, a._2)
}
}
So I thought of changing it and that's where I got stuck. The below code is failing for all the right reasons but can someone guide on how to implement something similar?
trait Opt[A] {
def map[B](f: A => B): Opt[B]
def flatMap[B](f: A => Opt[B]): Opt[B]
}
case class Osome[A, B](a: (A, B)) extends Opt[(A, B)] {
def flatMap[C, D](f: ((A, B)) => Opt[(C,D)]): Opt[(C, D)] = {
f(a._1, a._2)
}
def map [C, D] (f: ((A, B)) => (C, D)): Opt[(C, D)] = {
Osome(f(a._1, a._2))
}
}
The problem is, that you don't implement the map and flatMap methods of Opt in your Osome class, but you overload them. In your trait they both have one type parameter, but in your implementation they have two. Aside from that, your Osome would anyway not be a monad, as it restricts the resulting type of map and flatMap, where monads allow for arbitrary types.