Skip to content

Named version of mapN and friends #4792

@BalmungSan

Description

@BalmungSan

I wonder if we could have a Scala 3 only version of mapN and friends that work on NamedTuples instead of plain ones.

The motivation would be to fix the major usability issue with applicative composition, where operations and the names of their results are separated.

For example, imagine a very simple for comprehension like this:

final case class Config(foo: Foo, bar: Bar, baz: Baz, quax: Quax)
object Config {
  def load: IO[Config] =
    for {
      foo <- env(name = "FOO").as[Foo]
      bar <- env(name = "BAR").as[Bar]
      baz <- env(name = "BAZ").as[Baz]
      quax <- env(name = "QUAX").as[Quax]
    } yield Config(foo, bar, baz, quax)
}

One may realize that there is no dependency between each operation, and thus that instead of failing fast, we could try to read all env vars "concurrently" and accumulate all the errors instead:

def load: IO[Config] =
  (
    env(name = "FOO").as[Foo],
    env(name = "BAR").as[Bar],
    env(name = "BAZ").as[Baz],
    env(name = "QUAX").as[Quax]
  ).parMapN { case (foo, bar, baz, quax) =>
    Config(foo, bar, baz, quax)
  }

But, this disconnects the names (foo, bar, baz, quax) from the operations that produced them; and yes, in this case, you could just do parMapN(Config.apply) but that is besides the point.

Thus, it would be great if we could use NamedTuples and maybe Structural Types to get an API like this:

def load: IO[Config] =
  (
    foo = env(name = "FOO").as[Foo],
    bar = env(name = "BAR").as[Bar],
    baz = env(name = "BAZ").as[Baz],
    quax = env(name = "QUAX").as[Quax]
  ).namedParMapN { result =>
    Config(result.foo, result.bar, result.baz, result.quax)
  }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions