把单位还给错了吗?
Is it wrong to give the unit back?
我有以下功能:
private def constraintToJson(req: => Request[IO])
: EitherT[IO, Throwable, Unit]
= {
val err: EitherT[IO, Throwable, Unit] = EitherT.fromEither[IO](Left(new Exception("Not JSON format request.")))
req.contentType match {
case Some(s) =>
if (s != `Content-Type`(MediaType.`application/json`))
err
else
EitherT.fromEither[IO](Right(()))
case None =>
err
}
}
问题是,return一个错了Unit
是对还是有别的选择?
我认为 returning Unit
(包含在 Either
/EitherT
中)可能没问题,如果这是您计算的最后一步,并且它实际上不产生任何输出。在其他情况下(很可能包括您的),您应该 return 成功案例的一些价值,以便您可以进一步链接它。所以主要问题是:应该如何使用 constraintToJson
?如果 Content-Type
与 JSON 匹配,则您的案例明显怀疑是 returning Request
或其主体,因为这很可能是下一步将使用的数据。
简化问题
让我先通过删除 IO
来抽象化您的问题。
问题是具有如下签名的方法是否有用:
def validate[A](a: Request[A]): Either[Error,()] =
if(isOk(a.someProperty)) Left(()) else Right("invalid")
注意 Either[A,()] =:= Option[A]
,此方法检查请求值中是否存在任何 Error
,如果检测到 return,则检查此 Error
。
这样的方法能做什么?
检查并退出
我们可以编写一个程序来检查 Request
:
val someRequest: Request[X] = getRequestFrom("somewhere")
validate(someRequest).toLeft.foreach{error =>
println("invalid request: " + error
System.exit(-1)
}
System.exit(0)
在这种情况下 validate
有一个有用的签名。
检查并继续
我们也可以validate
一个请求,然后继续执行它:
val someRequest: Request[X] = getRequestFrom("somewhere")
validate(someRequest) match {
case Left(error) =>
println("invalid request: " + error)
System.exit(-1)
case Right(_) =>
println("executing request: " + someRequest.execute)
System.exit(0)
}
虽然此代码运行良好,但它在第二个 match
子句中丢弃了一个参数 (_
)。这是一些不良设计的指标。 case Right(_)
中的代码从外部获取值 someRequest
,并根据您编写代码的方式将其视为有效请求。
如果我们在 Left
情况下调用 someReuqest.execute
,我们可能会在执行无效 Request
时出现运行时异常。
根据我们所需的严谨程度,这可能是一种代码味道。
我们可以通过以下方式改进代码。
使用非单位 return 类型
我们可以通过简单地 returning 已检查的参数来绕过 returning Unit
。
这似乎有点多余,稍后我们将看到如何将 returned 值变成有用的东西。
但是让我们先看看这个选项。
def validateTyped[A](a: Request[A]): Either[Error,Request[A]] =
if(isOk(a.someProperty)) Left(a) else Right("invalid")
那么我们可以把勾选继续代码写成
val someRequest: Request[X] = getRequestFrom("somewhere")
validate(someRequestTyped) match {
case Left(error) =>
println("invalid request: " + error)
System.exit(-1)
case Right(validatedRequest) =>
//we now execute the validated request
println("executing request: " + validatedRequest.execute)
System.exit(0)
}
现在这稍微改进了代码。
我们不再在第二个 match
子句中丢弃 returned 值。
我们可以执行 validatedRequest
,因为我们在 validateRequest
的文档中读到 returned Right
值的格式是正确的,并且可以无错误地执行。
看起来不错吧?
但我们可以做得更好。
防止执行格式错误的请求
我们仍然可以通过更改 Request
类型来完全防止执行格式错误的 Request
来进一步改进它。
case class Request[A](query: String){
def validate: Either[Error,ValidRequest[A]] = ???
}
case class ValidRequest[A](query: String){
def execute: A
}
使用此代码,无法再调用 Request.execute
,因为现在必须进行验证。
否则代码将无法编译。
我们还注意到现在需要由 Request.validate
编辑的 Right
值 return。
它已成为获得 ValidRequest
.
的唯一来源
现在无法调用 execute
尚未验证的 Request
:
val someRequest: Request[X] = getRequestFrom("somewhere")
someRequest.validate match {
case Left(error) =>
println("invalid request: " + error)
System.exit(-1)
case Right(validatedRequest) =>
println("executing request: " + validatedRequest.execute)
System.exit(0)
}
结论
通过这种方式,我们将 return 编辑 Unit
的一种看起来很奇怪的方法变成了一种 return 有意义且需要的值的方法。
此外,无法 execute
未成功验证的 Request
。
我有以下功能:
private def constraintToJson(req: => Request[IO])
: EitherT[IO, Throwable, Unit]
= {
val err: EitherT[IO, Throwable, Unit] = EitherT.fromEither[IO](Left(new Exception("Not JSON format request.")))
req.contentType match {
case Some(s) =>
if (s != `Content-Type`(MediaType.`application/json`))
err
else
EitherT.fromEither[IO](Right(()))
case None =>
err
}
}
问题是,return一个错了Unit
是对还是有别的选择?
我认为 returning Unit
(包含在 Either
/EitherT
中)可能没问题,如果这是您计算的最后一步,并且它实际上不产生任何输出。在其他情况下(很可能包括您的),您应该 return 成功案例的一些价值,以便您可以进一步链接它。所以主要问题是:应该如何使用 constraintToJson
?如果 Content-Type
与 JSON 匹配,则您的案例明显怀疑是 returning Request
或其主体,因为这很可能是下一步将使用的数据。
简化问题
让我先通过删除 IO
来抽象化您的问题。
问题是具有如下签名的方法是否有用:
def validate[A](a: Request[A]): Either[Error,()] =
if(isOk(a.someProperty)) Left(()) else Right("invalid")
注意 Either[A,()] =:= Option[A]
,此方法检查请求值中是否存在任何 Error
,如果检测到 return,则检查此 Error
。
这样的方法能做什么?
检查并退出
我们可以编写一个程序来检查 Request
:
val someRequest: Request[X] = getRequestFrom("somewhere")
validate(someRequest).toLeft.foreach{error =>
println("invalid request: " + error
System.exit(-1)
}
System.exit(0)
在这种情况下 validate
有一个有用的签名。
检查并继续
我们也可以validate
一个请求,然后继续执行它:
val someRequest: Request[X] = getRequestFrom("somewhere")
validate(someRequest) match {
case Left(error) =>
println("invalid request: " + error)
System.exit(-1)
case Right(_) =>
println("executing request: " + someRequest.execute)
System.exit(0)
}
虽然此代码运行良好,但它在第二个 match
子句中丢弃了一个参数 (_
)。这是一些不良设计的指标。 case Right(_)
中的代码从外部获取值 someRequest
,并根据您编写代码的方式将其视为有效请求。
如果我们在 Left
情况下调用 someReuqest.execute
,我们可能会在执行无效 Request
时出现运行时异常。
根据我们所需的严谨程度,这可能是一种代码味道。
我们可以通过以下方式改进代码。
使用非单位 return 类型
我们可以通过简单地 returning 已检查的参数来绕过 returning Unit
。
这似乎有点多余,稍后我们将看到如何将 returned 值变成有用的东西。
但是让我们先看看这个选项。
def validateTyped[A](a: Request[A]): Either[Error,Request[A]] =
if(isOk(a.someProperty)) Left(a) else Right("invalid")
那么我们可以把勾选继续代码写成
val someRequest: Request[X] = getRequestFrom("somewhere")
validate(someRequestTyped) match {
case Left(error) =>
println("invalid request: " + error)
System.exit(-1)
case Right(validatedRequest) =>
//we now execute the validated request
println("executing request: " + validatedRequest.execute)
System.exit(0)
}
现在这稍微改进了代码。
我们不再在第二个 match
子句中丢弃 returned 值。
我们可以执行 validatedRequest
,因为我们在 validateRequest
的文档中读到 returned Right
值的格式是正确的,并且可以无错误地执行。
看起来不错吧? 但我们可以做得更好。
防止执行格式错误的请求
我们仍然可以通过更改 Request
类型来完全防止执行格式错误的 Request
来进一步改进它。
case class Request[A](query: String){
def validate: Either[Error,ValidRequest[A]] = ???
}
case class ValidRequest[A](query: String){
def execute: A
}
使用此代码,无法再调用 Request.execute
,因为现在必须进行验证。
否则代码将无法编译。
我们还注意到现在需要由 Request.validate
编辑的 Right
值 return。
它已成为获得 ValidRequest
.
现在无法调用 execute
尚未验证的 Request
:
val someRequest: Request[X] = getRequestFrom("somewhere")
someRequest.validate match {
case Left(error) =>
println("invalid request: " + error)
System.exit(-1)
case Right(validatedRequest) =>
println("executing request: " + validatedRequest.execute)
System.exit(0)
}
结论
通过这种方式,我们将 return 编辑 Unit
的一种看起来很奇怪的方法变成了一种 return 有意义且需要的值的方法。
此外,无法 execute
未成功验证的 Request
。