0

I'm just learning FP, so maybe i'm doing this completely wrong.

Using cats, cats-effects, fs2, fs2-io.

Following code takes takes port as an argument then creates Server Socket and Client Socket connected to 127.0.0.1 on given port.

What I need this function to return is allocated Server Socket port.

Question: how can I do Int => F[Int] while running fs2.Stream under the method's hood?

I'm using cats-effects IOApp to run the program.

My code:

trait TCP[F[_]] {
    def createTunnel(localPort: Int): F[TCP.CreatedTunnel]
}
object TCP {
    implicit def apply[F[_]](implicit ev: TCP[F]): TCP[F] = ev

    final case class CreatedTunnel(remotePort: Int) extends AnyVal

    def impl[F[_]: Applicative : ContextShift : ConcurrentEffect](socketGroup: SocketGroup): TCP[F] = new TCP[F] {

        override def createTunnel(localPort: Int): F[CreatedTunnel] = {

            val localBindAddress = Deferred[F, InetSocketAddress]

            val stream = Stream
                .eval(socketGroup.serverWithLocalAddress[F](new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0)))
            .flatMap {
                  case Left(localAddress) =>
                      Stream.eval_(localBindAddress.flatMap(_.complete(localAddress)))
                  case Right(serverSocket) =>
                      Stream.resource(serverSocket.map { socket =>
                          Stream
                              .eval(socketGroup.client[F](new InetSocketAddress("127.0.0.1", localPort)))
                          .map { clientSocket =>
                                clientSocket.reads(1024).through(socket.writes())
                                socket.reads(1024).through(clientSocket.writes())
                            }
                      })
              }

            // -------------------------------------------
            // (!!!) I need to return this, 
            //       but i need to run stream above first
            // -------------------------------------------
            localBindAddress.map(_.get.map(addr => TCP.CreatedTunnel(addr.getPort)))

        }
    }
}
pool
  • 11
  • 1
  • 1
  • 1
    `stream.compile.drain >> localBindAddress.map(...)`? – Oleg Pyzhcov Dec 24 '19 at 09:56
  • @OlegPyzhcov, thanks it does the compiler trick but i'm getting error `could not find implicit value for parameter compiler: fs2.Stream.Compiler[[x]Any,G] [error] stream.compile.drain >> localBindAddress.map(_.get.map(addr => TCP.CreatedTunnel(addr.getPort))) [error] ^` the other stackoverflow issue (https://stackoverflow.com/questions/56329032/fs2-stream-compiler-is-not-found-could-not-find-implicit-value-compilerxfx) suggest adding Sync[F] but it doesnt help in my case – pool Dec 24 '19 at 16:00
  • this could be a misshap on your stream type - you seem to be getting `Stream[Any, Unit]` or something. There are definitely issues in your code that I can spot unrelated to this too, so if you're just learning, I suggest you visit [fs2 gitter chat](https://gitter.im/functional-streams-for-scala/fs2) to get help quicker. – Oleg Pyzhcov Dec 25 '19 at 12:07

0 Answers0