我正在尝试在我的项目中开始使用免费的 monad,我正在努力让它变得优雅。
假设我有两个上下文(实际上我有更多) -Receipt而且User- 都对数据库进行了操作,我希望将它们的解释器分开并在最后一刻组合它们。
为此,我需要为每个操作定义不同的操作,并使用Coproduct.
这是我经过几天的谷歌搜索和阅读后的结果:
// Receipts
sealed trait ReceiptOp[A]
case class GetReceipt(id: String) extends ReceiptOp[Either[Error, ReceiptEntity]]
class ReceiptOps[F[_]](implicit I: Inject[ReceiptOp, F]) {
def getReceipt(id: String): Free[F, Either[Error, ReceiptEntity]] = Free.inject[ReceiptOp, F](GetReceipt(id))
}
object ReceiptOps {
implicit def receiptOps[F[_]](implicit I: Inject[ReceiptOp, F]): ReceiptOps[F] = new ReceiptOps[F]
}
// Users
sealed trait UserOp[A]
case class GetUser(id: String) extends UserOp[Either[Error, User]]
class UserOps[F[_]](implicit I: Inject[UserOp, F]) {
def getUser(id: String): Free[F, Either[Error, User]] = …Run Code Online (Sandbox Code Playgroud) 我已经尝试掌握 IO monad 一段时间了,这是有道理的。如果我没记错的话,目标是将副作用的描述和实际执行分开。如下例所示,Scala 有一种方法可以获取非引用透明的环境变量。出现了两个问题。
问题一:这个是参照透明的吗
问题 2:如何正确(基于单位/属性)测试这一点?不可能检查相等性,因为它将检查内存引用,并且不可能检查内部函数,因为如果我没有弄错的话,函数比较是不可能的。但是,我不想在单元测试中运行实际的副作用。另外,这是设计错误还是 IO monad 的误用?
case class EnvironmentVariableNotFoundException(message: String) extends Exception(message)
object Env {
def get(envKey: String): IO[Try[String]] = IO.unit.flatMap((_) => IO.pure(tryGetEnv(envKey)))
private[this] def tryGetEnv(envKey: String): Try[String] =
Try(System.getenv(envKey))
.flatMap(
(x) =>
if (x == null) Failure(EnvironmentVariableNotFoundException(s"$envKey environment variable does not exist"))
else Success(x)
)
}
Run Code Online (Sandbox Code Playgroud) 我想知道是否有办法把List[Kleisli[Option, Int, Int]]到Kleisli[Option, Int, List[Int]].
特别是我有像这样形成的kleisli列表:
def k(a: String) = Kleisli[Option, Int, Int](m => Some(a.length * m))
val kList = List("hi", "hello").map(k)
Run Code Online (Sandbox Code Playgroud)
我所做的是以下内容
Kleisli[Option, Int, List[Int]](m => kList.map(_.run(m)).sequence)
Run Code Online (Sandbox Code Playgroud)
这是非常混乱,没有表现力,需要大量的手工工作.
有没有更好的办法?
我有一个计算数字类型的平方和的函数,如下所示。
import cats.syntax.functor._
import cats.syntax.applicative._
import cats.{Id, Monad}
import scala.language.higherKinds
object PowerOfMonads {
/**
* Ultimate sum of squares method
*
* @param x First value in context
* @param y Second value in context
* @tparam F Monadic context
* @tparam T Type parameter in the Monad
* @return Sum of squares of first and second values in the Monadic context
*/
def sumOfSquares[F[_]: Monad, A, T >: A](x: F[A], y: F[A])(implicit num: Numeric[T]) : F[T] = {
def …Run Code Online (Sandbox Code Playgroud) 我正在从scala-exercises学习 Cats 。
想知道如何使用高阶类型,有一些尝试:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
def someThingFail1[In]() = new Functor[Function1[In, _]] {
override def map[A, B](fa: Function1[In, A])(f: A => B): Function1[In, B] = ???
}
def someThingFail2[In]() = new Functor[Either[In, _]] {
override def map[A, B](fa: Either[In, A])(f: A => B): Either[In, B] = ???
}
def someThingFail3() = new Functor[List[_]] {
override def map[A, B](fa: List[A])(f: A => B): List[B] = ???
}
//only this …Run Code Online (Sandbox Code Playgroud) 我只是尝试编译以下代码:
import cats.Monoid
import cats.instances.boolean._ // for Monoid
import cats.instances.int._ // for Monoid
import cats.instances.list._ // for Monoid
import cats.instances.string._ // for Monoid
import cats.syntax.apply._ // for imapN
import cats.syntax.semigroup._
case class Cat2(name: String, yearOfBirth: Int, favoriteFoods: List[String])
object FancyFunctor {
val tupleToCat: (String, Int, List[String]) => Cat2 =
Cat2.apply _
val catToTuple: Cat2 => (String, Int, List[String]) =
cat => (cat.name, cat.yearOfBirth, cat.favoriteFoods)
implicit val catMonoid: Monoid[Cat2] = (
Monoid[String],
Monoid[Int],
Monoid[List[String]]
).imapN(tupleToCat)(catToTuple)
def main(args: Array[String]): Unit = { …Run Code Online (Sandbox Code Playgroud) 即使终端 SBT 工作正常,在 IntelliJ IDEA 中使用 Scala 的 Cats 库也会导致错误。我们能解决这个问题吗?或者一些不切换编辑器的解决方法?
我正在使用该BlazeClientBuilder[IO].resource方法来获取Client[IO]. 现在,我想模拟客户端进行单元测试,但不知道该怎么做。有没有一个好的方法来嘲笑这个,我会怎么做?
class ExternalCall(val resource: Resource[IO, Client[IO]], externalServiceUrl: Uri) {
def retrieveData: IO[Either[Throwable, String]] = {
for {
req <- IO(Request[IO](Method.GET, uri = externalServiceUrl))
response <- resource.use(client => {
client.fetch[String](req)(httpResponse => {
if (!httpResponse.status.isSuccess)
throw new Exception(httpResponse.status.reason)
else
httpResponse.as[String]
})
})
} yield Right(response)
}
}
Run Code Online (Sandbox Code Playgroud)
来电代码
new ExternalCall(BlazeClientBuilder[IO](global).resource).retrieveData
Run Code Online (Sandbox Code Playgroud) unsafeRunSync问题:使用暂停是否安全IO?例如
val io: IO[Unit] = //...
val io2: IO[Unit] = IO(io.unsafeRunSync)
Run Code Online (Sandbox Code Playgroud)
我这样做的原因是我有一些参数化的类,F[_]: Effect它就像一个缓存:
import cats.effect.Effect
final class MyChache[F[_]](implicit F: Effect[F]) {
private val cache = new ConcurrentHashMap[Int, String]
def getOrCreate(key: Int): F[String] = F delay {
cache.computeIfAbsent(
key,
k => longRunningEffecfulComputation(k).toIO.unsafeRunSync() // <-- Here
)
}
}
object MyCache {
def longRunningEffecfulComputation[F[_] : Effect](key: Int): F[String] = {
//...
}
}
Run Code Online (Sandbox Code Playgroud)
重点是我只想为每个键运行一次长时间运行的有效计算(这是相当罕见的)。然而,我希望在检索现有密钥时保持非阻塞。
ConcurrentHashMap似乎是一个完美的选择,但它需要这种带有运行和暂停效果的丑陋技巧。还有更好的方法吗?
假设我有:
val x1: Either[String, Int] = Right(1)
val x2: Either[String, Float] = Left("Nope")
val x3: Either[String, Double] = Left("Not Today")
Run Code Online (Sandbox Code Playgroud)
我想将它们组合在一起并得到一个Either[NonEmptyList[String], (Int, Float, Double)]. 为此,我目前正在执行以下操作:
import cats.syntax.all._
(
x1.toValidatedNel,
x2.toValidatedNel,
x3.toValidatedNel
).tupled
.toEither
Run Code Online (Sandbox Code Playgroud)
这可以完成工作,但有点乏味。有没有办法toValidatedNel只调用一次来做到这一点?就像是:
(x1, x2, x3)
.fooMap(_.toValidatedNel)
.tupled
.toEither
Run Code Online (Sandbox Code Playgroud)
fooMap猫身上是否存在这样的东西?或者我们需要HLists那个吗?
scala ×10
scala-cats ×10
monads ×2
applicative ×1
concurrency ×1
either ×1
free-monad ×1
implicit ×1
io-monad ×1
kleisli ×1
mockito ×1
tuples ×1