为无标签代数编写定律或单元测试
Write laws or unit test for tagless algebras
我已经写了两个无标签代数,我想为其中一个写出定律。
代数如下:
@newtype case class Variable(v: String)
@newtype case class Value(v: String)
trait Environment[F[_]] {
def get(v: Variable): F[Option[Value]]
}
@newtype case class DbUrl(v: String)
@newtype case class DbUser(v: String)
@newtype case class DbPw(v: String)
final case class DbParams(url: DbUrl, user: DbUser, pw: DbPw)
trait DbConnector[F[_]] {
def read(url: DbUrl, user: DbUser, pw: DbPw): F[DbParams]
}
口译员如下:
object Environment {
def apply[F[_]](implicit ev: Environment[F]): ev.type = ev
def impl[F[_] : Sync]: Environment[F] = new Environment[F] {
override def get(v: Variable): F[Option[Value]] =
Sync[F].delay(sys.env.get(v.v).map(a => Value(a)))
}
}
final case class LiveDbConnector[F[_] : MonadError[*[_], Throwable]](env: Environment[F]) extends DbConnector[F] {
override def read(url: DbUrl, user: DbUser, pw: DbPw): F[DbParams] =
(for {
a <- OptionT(env.get(Variable(url.v)))
b <- OptionT(env.get(Variable(user.v)))
c <- OptionT(env.get(Variable(pw.v)))
} yield DbParams(DbUrl(a.v), DbUser(b.v), DbPw(c.v)))
.value
.flatMap {
case Some(v) => v.pure[F]
case None => DbSettingError.raiseError[F, DbParams]
}
}
object DbConnector {
def impl[F[_] : MonadError[*[_], Throwable]](env: Environment[F])
: DbConnector[F] =
new LiveDbConnector[F](env)
函数式编程中有Monoid、Monads等规律
我的问题是:
- 我的代数需要规律还是编写单元测试就足够了?
- 规律和单元测试有什么区别?
- 我应该如何为
DbConnector
代数写法
您混淆了数学定律和稳健性与程序的正确性。
首先,Monad、Monoid、Fold 等不是定律。它们是来自 Category Theory 的数学“结构”。这些“结构”具有它们需要遵守的某些特性,以使其健全和正确。例如,这些属性中的一组称为 Monadic Laws
数学中的任何 Monadic 结构都需要遵守这 3 条规则:
- 留下身份
- 正确身份
- 关联性
Monad、flatMap 和 pure(或数学中的 join 和 return)的操作必须符合这些法则才能正常运行。即 pure(a).flatMap(f) == f(a)
、M.flatMap(pure) == M
等
在函数式编程中,Monads 是从这些数学结构和法则衍生出来的一种设计模式。他们描述了一些“可组合的计算”。在猫中,Monad 被定义为 typeclass。此结构符合上述规则。
How should I write laws for DbConnector algebra
如果你想证明定律,你可能需要使用定理证明器,在 Scala 中确实没有明确编写或测试定律的方法,但你总是可以编写单元测试来确保你的 Monad 遵守这些法,即 testFlatMapLeftIdentity(...)
等
Do my algebras need laws or writing the unit tests is enough?
简而言之,我想不出代数有明确规律的情况,除非你的代数描述了一些数学运算集。例如,在您的代码中,上下文绑定 MonadError[*[_], Throwable]]
需要范围内的 Monad[F]
,这需要遵守这些法律,假设您使用的是常见的 IO, Task, Future
等,猫已经做到了所以你不需要担心这些法律。如果您决定实现自己的效果类型或编写新的 Monad 实现,您将需要确保遵守这些法则,但您的代码中没有任何内容需要您担心它们。为这个代数编写良好的单元测试就足够了。
我已经写了两个无标签代数,我想为其中一个写出定律。
代数如下:
@newtype case class Variable(v: String)
@newtype case class Value(v: String)
trait Environment[F[_]] {
def get(v: Variable): F[Option[Value]]
}
@newtype case class DbUrl(v: String)
@newtype case class DbUser(v: String)
@newtype case class DbPw(v: String)
final case class DbParams(url: DbUrl, user: DbUser, pw: DbPw)
trait DbConnector[F[_]] {
def read(url: DbUrl, user: DbUser, pw: DbPw): F[DbParams]
}
口译员如下:
object Environment {
def apply[F[_]](implicit ev: Environment[F]): ev.type = ev
def impl[F[_] : Sync]: Environment[F] = new Environment[F] {
override def get(v: Variable): F[Option[Value]] =
Sync[F].delay(sys.env.get(v.v).map(a => Value(a)))
}
}
final case class LiveDbConnector[F[_] : MonadError[*[_], Throwable]](env: Environment[F]) extends DbConnector[F] {
override def read(url: DbUrl, user: DbUser, pw: DbPw): F[DbParams] =
(for {
a <- OptionT(env.get(Variable(url.v)))
b <- OptionT(env.get(Variable(user.v)))
c <- OptionT(env.get(Variable(pw.v)))
} yield DbParams(DbUrl(a.v), DbUser(b.v), DbPw(c.v)))
.value
.flatMap {
case Some(v) => v.pure[F]
case None => DbSettingError.raiseError[F, DbParams]
}
}
object DbConnector {
def impl[F[_] : MonadError[*[_], Throwable]](env: Environment[F])
: DbConnector[F] =
new LiveDbConnector[F](env)
函数式编程中有Monoid、Monads等规律
我的问题是:
- 我的代数需要规律还是编写单元测试就足够了?
- 规律和单元测试有什么区别?
- 我应该如何为
DbConnector
代数写法
您混淆了数学定律和稳健性与程序的正确性。
首先,Monad、Monoid、Fold 等不是定律。它们是来自 Category Theory 的数学“结构”。这些“结构”具有它们需要遵守的某些特性,以使其健全和正确。例如,这些属性中的一组称为 Monadic Laws
数学中的任何 Monadic 结构都需要遵守这 3 条规则:
- 留下身份
- 正确身份
- 关联性
Monad、flatMap 和 pure(或数学中的 join 和 return)的操作必须符合这些法则才能正常运行。即 pure(a).flatMap(f) == f(a)
、M.flatMap(pure) == M
等
在函数式编程中,Monads 是从这些数学结构和法则衍生出来的一种设计模式。他们描述了一些“可组合的计算”。在猫中,Monad 被定义为 typeclass。此结构符合上述规则。
How should I write laws for DbConnector algebra
如果你想证明定律,你可能需要使用定理证明器,在 Scala 中确实没有明确编写或测试定律的方法,但你总是可以编写单元测试来确保你的 Monad 遵守这些法,即 testFlatMapLeftIdentity(...)
等
Do my algebras need laws or writing the unit tests is enough?
简而言之,我想不出代数有明确规律的情况,除非你的代数描述了一些数学运算集。例如,在您的代码中,上下文绑定 MonadError[*[_], Throwable]]
需要范围内的 Monad[F]
,这需要遵守这些法律,假设您使用的是常见的 IO, Task, Future
等,猫已经做到了所以你不需要担心这些法律。如果您决定实现自己的效果类型或编写新的 Monad 实现,您将需要确保遵守这些法则,但您的代码中没有任何内容需要您担心它们。为这个代数编写良好的单元测试就足够了。