I was trying to teach myself more functional programming using Scala and I’m now looking to build an app using cats
and cats-effect
.
The program I have in mind is a WebSocket client. It connects to an endpoint, sends an initial message(s). Then it mostly reacts to the messages sent by the server. No user interaction is required.
This line of though lead me to also include fs2
, sttp
libraries and write the scaffolding below.
- Is this the right direction as far as writing functional Scala apps?
- How can I develop the
WebSocketApp
trait to be able to build an using composable functions? I need to parse the incoming messages, handle then, call the database, prepare response, encode it into JSON, etc. How can I do this leveragingcats
and functional paradigm? - How can I further extend this program to work with multiple WebSocket endpoints/stream in parallel?
import fs2._
import cats.effect._
import sttp.ws.WebSocketFrame
import sttp.client3._
import sttp.client3.httpclient.fs2.HttpClientFs2Backend
import sttp.capabilities.fs2.Fs2Streams
import sttp.model.Uri
trait WebSocketApp:
def init: Seq[WebSocketFrame]
def apply(frame: WebSocketFrame): IO[Seq[WebSocketFrame]]
object Main extends IOApp:
val app: WebSocketApp = ???
val uri: Uri = ???
def wsFramePipe(
app: WebSocketApp
): Pipe[IO, WebSocketFrame.Data[?], WebSocketFrame] = { input =>
Stream.emits(app.init) ++
input.evalMap(app.apply).flatMap(Stream.emits)
}
def wsStart(app: WebSocketApp): IO[Unit] = HttpClientFs2Backend
.resource[IO]()
.use { backend =>
basicRequest
.response(asWebSocketStream(Fs2Streams[IO])(wsFramePipe(app)))
.get(uri)
.send(backend)
.void
}
override def run(args: List[String]): IO[ExitCode] =
for {
_ <- IO.println("hello world")
_ <- wsStart(app)
} yield ExitCode.Success