-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
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)
}