typelevel / typelevel/cats

Foldable for tuples?

Open
#3,812 1 comment 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Scala
Stars
5.5k
Forks
1.2k
Avg merge
2d 10h
Merged PRs (30d)
5

Description

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 As 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 As and puts the As 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.

source

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"}
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Review the tuple Foldable instance introduced around cats PR 3299 and reproduce the toList((1,2)) example with the Cats 2.3.x and 2.4.x behavior. Check doobie's linked Text.scala derivation around line 186 to understand the implicit-resolution impact. Done means reaching a documented decision about tuple Foldable behavior and its compatibility effects.

Written by the indexing model from the issue text.

Assessment

Tech stack
scala
Domain
api
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.