I am playing with tagless final in scala. I use pureconfig to load the configuration and then use the configuration values to set the server port and host. Snippet
def create[F[_]: Async] =
for {
config <- ConfigSource.default.at("shopkart").load[AppConfig]
httpApp = EndpointApp.make[F]
server <- BlazeServerBuilder[F]
.bindHttp(port = config.http.port, host = config.http.host)
.withHttpApp(httpApp)
.resource
} yield server
The compilation error is ambiguous to me. This is the compilation error.
type mismatch;
[error] found : cats.effect.kernel.Resource[F,Unit]
[error] required: scala.util.Either[?,?]
[error] server <- BlazeServerBuilder[F]
[error] ^
[error] one error found
I understand that the ConfigSource.default.at("shopkart").load[AppConfig]
returns Either[ConfigReaderFailures, AppConfig]. But within the context of for-comprehension, it is an instance of AppConfig
. So, why in the following line where BlazeServerbuilder an Either is expected ?
My understanding is with in the context of for-comprehension, these are two different instances. Also, I came across a similar example in scala pet store https://github.com/pauljamescleary/scala-pet-store/blob/master/src/main/scala/io/github/pauljamescleary/petstore/Server.scala#L28
How to de-sugar for to understand this error better?
CodePudding user response:
The code below that you would have got if you have used flatMap/map instead of for-comprehension.
ConfigSource.default.at("shopkart").load[AppConfig] // Either[E, AppConfig]
.flatMap { config => // in flatMap you should have the same type of Monad
BlazeServerBuilder[F] // Resource[F, BlazeServerBilder[F]]
.bindHttp(port = config.http.port, host = config.http.host)
.withHttpApp(EndpointApp.make[F])
.resource
}
The cause of your error that you can't use different types of a monad in one for-comprehension block. If you need that you should convert your monads to the same type. In your case the easiest way is converting your Either
to Resource[F, AppConfig]
. But you have to consider using F that can understand an error type of Either, like MonadError to handle error from Either and convert it to F. After you can use Resource.eval
that expects F. I see that you use Async
, so you could use Async[F].fromEither(config)
for that.
def create[F[_]: Async] =
for {
config <- Resource.eval(
Async[F].fromEither(ConfigSource.default.at("shopkart").load[AppConfig])
)
httpApp = EndpointApp.make[F]
server <- BlazeServerBuilder[F]
.bindHttp(port = config.http.port, host = config.http.host)
.withHttpApp(httpApp)
.resource
} yield server