thlim thlim - 2 months ago 17
Scala Question

Return Try or Future As Generic Container

Instead of using overloaded methods in

object JSONSourceLoaderUtil
, I want to switch to pattern matching style. How do I handle the resultant
Try[JValue]
and
Future[JValue]
as
F[JValue]
?

Imports and case classes,

import scalaz._
import Scalaz._
import org.json4s.JsonAST.{JObject, JValue}

trait DataSource
case class LocalFile(input: File) extends DataSource
case class RemoteResource(url: String, req: JValue) extends DataSource


What I have now,

object JSONSourceLoaderUtil {

def jsonFrom[F[_], S <: DataSource](source: S)(f: S => F[JValue])(implicit ev: Monad[F]): F[JValue] = ev.bind(ev.point(source))(f)

def extractFrom(source: RemoteResource): Future[JValue] = {
Future( ... ).flatMap(input => Future.fromTry(Parser.parseFromChannel(Channels.newChannel(input))))
}

def extractFrom(source: LocalFile): Try[JValue] = Parser.parseFromFile(source.input)
}


How do I convert to pattern matching style? Is there a another way to do this if I have painted myself into a corner? Thanks.

object JSONSourceLoaderUtil {

def jsonFrom[F[_], S <: DataSource](source: S)(f: S => F[JValue])(implicit ev: Monad[F]): F[JValue] = ev.bind(ev.point(source))(f)

def extractFrom(source: DataSource): F[JValue] = source match {
case RemoteResource(url, request) => Future( ... )
.flatMap(input => Future.fromTry(Parser.parseFromChannel(Channels.newChannel(input))))) // cannot convert Future to F

case LocalFile(input) => Parser.parseFromFile(input) // cannot convert Try to F
}
}

Answer

Your desired F depends on the type of the data source. So why not make this explicit?

trait DataSource[F[_]] {
  def extract: F[JValue]
}

case class LocalFile(input: File) extends DataSource[Try] {
  def extract = Parser.parseFromFile(input)
}

case class RemoteResource(url: String, req: JValue) extends DataSource[Future] {
  def extract = Future( ... )
    .flatMap(input => Future.fromTry(Parser.parseFromChannel(Channels.newChannel(input)))))
}

Removing the extract method and writing

def extractFrom[F[_]](source: DataSource[F]): F[JValue] = source match {
    case RemoteResource(url, request) => Future( ... )
      .flatMap(input => Future.fromTry(Parser.parseFromChannel(Channels.newChannel(input)))))

    case LocalFile(input) => Parser.parseFromFile(input)
  }
}

should also probably work, at least in Scala 2.12. But I find the first solution to be cleaner.