cats
cats copied to clipboard
Foldable for tuples?
Hey there.
In https://github.com/typelevel/cats/pull/3299 (I think. at least in 2.4.x) tuples gained a Foldable
(among others, naturally) instance, which only considers the right-most element.
I guess I'm opening up a discussion of whether this makes any sense?
import cats.syntax._
def toList[F[_]: Foldable, A](fa: F[A]): List[A] =
Foldable[F].toList(fa)
println(toList((1,2))) // List(2)
The issue I see is I associate Foldable
with "handle all values" rather than "handle a subset of a value".
How this breaks the Text
type class in doobie for tuples
Onto the discussion of how I ended up looking into this. I tried to upgrade cats to 2.4.2 from 2.3.1, and it broke our code in a strange way.
We use doobie, which among other things offers a way to stream A
s to postgres via a (tab-separated) textual format through the Text
type class.
Let's examine a derivation rule which now causes problems:
It takes a Foldable F
of A
s and puts the A
s into a postgres array, again in the textual representation. Crucially, this is prioritized before the macro which composes instances for Text
for for product types, which is what we used to hit.
object Text {
// ... simplified, it's really in a parent
implicit def foldableInstance[F[_]: Foldable, A](implicit ev: Text[A]): Text[F[A]] =
iterableInstance[List, A].contramap(_.toList)
}
Here is a minified example, where you can see the resolved implicits before and after
object Tester extends App {
import cats.UnorderedFoldable
import doobie.postgres.Text
def p[A: Text](a: A): Unit =
println(Text[A].encode(a))
private val tuple: (Int, Long, String) = (1, 2L, "hello")
p(tuple)
// cats 2.3.x it picked this
p(tuple)(Text.generic)
// output: 1 2 hello
// cats 2.4.x it picks this
p(tuple)(Text.foldableInstance(UnorderedFoldable.catsUnorderedFoldableInstancesForTuple3, Text[String]))
// output: {"hello"}
}
Thanks for documenting this issue and I agree with you that behaviour is weird. Not sure what we can do here (except that you'll have to avoid using Foldable for now)...