- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我想定义一个接受 HList
的函数,它的元素是这样的,对于每个元素 t
,都有一个类型 T
这样 t: Either[String, T]
。我们将调用 validate
的函数应该具有以下行为:
Right
,则返回用右投影映射参数的结果的Right
。Left[List[String]]
,其中列表包含参数中每个 Left
的左投影。例子:
validate (Right (42) :: Right (3.14) :: Right (false) :: HNil)
>> Right (42 :: 3.14 :: false :: HNil)
validate (Right (42) :: Left ("qwerty") :: Left ("uiop") :: HNil)
>> Left (List ("qwerty", "uiop"))
示例用例:
case class Result (foo: Foo, bar: Bar, baz: Baz, qux: Qux)
def getFoo: Either[String, Foo] = ???
def getBar: Either[String, Bar] = ???
def getBaz: Either[String, Baz] = ???
def getQux: Either[String, Qux] = ???
def createResult: Either[String, Result] = {
validate (getFoo :: getBar :: getBaz :: getQux :: HNil) match {
case Right (foo :: bar :: baz :: qux :: HNil) => Right (Result (foo, bar, baz, qux))
case Left (errors) => Left ("The following errors occurred:\n" + errors.mkString ("\n"))
}
}
最佳答案
我假设在整个回答过程中我们都有这样的测试数据:
scala> import shapeless.{::, HNil}
import shapeless.{$colon$colon, HNil}
scala> type In = Either[String, Int] :: Either[String, String] :: HNil
defined type alias In
scala> val good: In = Right(123) :: Right("abc") :: HNil
good: In = Right(123) :: Right(abc) :: HNil
scala> val bad: In = Left("error 1") :: Left("error 2") :: HNil
bad: In = Left(error 1) :: Left(error 2) :: HNil
有很多方法可以做到这一点。我可能会使用一个自定义类型类来强调实例的归纳构建方式:
import shapeless.HList
trait Sequence[L <: HList] {
type E
type Out <: HList
def apply(l: L): Either[List[E], Out]
}
object Sequence {
type Aux[L <: HList, E0, Out0 <: HList] = Sequence[L] { type E = E0; type Out = Out0 }
implicit def hnilSequence[E0]: Aux[HNil, E0, HNil] = new Sequence[HNil] {
type E = E0
type Out = HNil
def apply(l: HNil): Either[List[E], HNil] = Right(l)
}
implicit def hconsSequence[H, T <: HList, E0](implicit
ts: Sequence[T] { type E = E0 }
): Aux[Either[E0, H] :: T, E0, H :: ts.Out] = new Sequence[Either[E0, H] :: T] {
type E = E0
type Out = H :: ts.Out
def apply(l: Either[E0, H] :: T): Either[List[E0], H :: ts.Out] =
(l.head, ts(l.tail)) match {
case (Right(h), Right(t)) => Right(h :: t)
case (Left(eh), Left(et)) => Left(eh :: et)
case (Left(eh), _) => Left(List(eh))
case (_, Left(et)) => Left(et)
}
}
}
然后你可以这样写validate
:
def validate[L <: HList](l: L)(implicit s: Sequence[L]): Either[List[s.E], s.Out] = s(l)
然后像这样使用它:
scala> validate(good)
res0: scala.util.Either[List[String],Int :: String :: shapeless.HNil] = Right(123 :: abc :: HNil)
scala> validate(bad)
res1: scala.util.Either[List[String],Int :: String :: shapeless.HNil] = Left(List(error 1, error 2))
请注意,静态类型是正确的。
你也可以用 Poly2
折叠起来更简洁一些。
import shapeless.Poly2
object combine extends Poly2 {
implicit def eitherCase[H, T, E, OutT <: HList]:
Case.Aux[Either[E, H], Either[List[E], OutT], Either[List[E], H :: OutT]] = at {
case (Right(h), Right(t)) => Right(h :: t)
case (Left(eh), Left(et)) => Left(eh :: et)
case (Left(eh), _) => Left(List(eh))
case (_, Left(et)) => Left(et)
}
}
然后:
scala> good.foldRight(Right(HNil): Either[List[String], HNil])(combine)
res2: scala.util.Either[List[String],Int :: String :: shapeless.HNil] = Right(123 :: abc :: HNil)
scala> bad.foldRight(Right(HNil): Either[List[String], HNil])(combine)
res3: scala.util.Either[List[String],Int :: String :: shapeless.HNil] = Left(List(error 1, error 2))
我想这可能是“正确”的答案,前提是您只想坚持使用 Shapeless。 Poly2
方法仅依赖于隐式解析的一些奇怪细节(例如,我们无法将 combine
定义为 val
),我个人认为不太喜欢。
最后你可以使用 Kittens库,支持排序和遍历 hlists:
scala> import cats.instances.all._, cats.sequence._
import cats.instances.all._
import cats.sequence._
scala> good.sequence
res4: scala.util.Either[String,Int :: String :: shapeless.HNil] = Right(123 :: abc :: HNil)
scala> bad.sequence
res5: scala.util.Either[String,Int :: String :: shapeless.HNil] = Left(error 1)
请注意,这不会累积错误。
如果你想要尽可能完整的 Typelevel 体验,我想你可以向 Kittens 添加一个 parSequence
操作,它会通过 Parallel
实例映射为 eithers 的 hlist 累积错误它们被Validated
(参见my blog post here了解更多关于它是如何工作的细节)。不过,Kittens 目前不包含此内容。
如果您想要 parSequence
,那么您自己编写它实际上并不是一场噩梦:
import shapeless.HList, shapeless.poly.~>, shapeless.ops.hlist.{Comapped, NatTRel}
import cats.Parallel, cats.instances.all._, cats.sequence.Sequencer
def parSequence[L <: HList, M[_], P[_], PL <: HList, Out](l: L)(implicit
cmp: Comapped[L, M],
par: Parallel.Aux[M, P],
ntr: NatTRel[L, M, PL, P],
seq: Sequencer.Aux[PL, P, Out]
): M[Out] = {
val nt = new (M ~> P) {
def apply[A](a: M[A]): P[A] = par.parallel(a)
}
par.sequential(seq(ntr.map(nt, l)))
}
然后:
scala> parSequence(good)
res0: Either[String,Int :: String :: shapeless.HNil] = Right(123 :: abc :: HNil)
scala> parSequence(bad)
res1: Either[String,Int :: String :: shapeless.HNil] = Left(error 1error 2)
请注意,这确实累积了错误,但通过连接字符串。在列表中累积错误的 Cats 惯用方法如下所示:
scala> import cats.syntax.all._
import cats.syntax.all._
scala> val good = 123.rightNel[String] :: "abc".rightNel[String] :: HNil
good: Either[cats.data.NonEmptyList[String],Int] :: Either[cats.data.NonEmptyList[String],String] :: shapeless.HNil = Right(123) :: Right(abc) :: HNil
scala> val bad = "error 1".leftNel[String] :: "error 2".leftNel[Int] :: HNil
bad: Either[cats.data.NonEmptyList[String],String] :: Either[cats.data.NonEmptyList[String],Int] :: shapeless.HNil = Left(NonEmptyList(error 1)) :: Left(NonEmptyList(error 2)) :: HNil
scala> parSequence(good)
res3: Either[cats.data.NonEmptyList[String],Int :: String :: shapeless.HNil] = Right(123 :: abc :: HNil)
scala> parSequence(bad)
res4: Either[cats.data.NonEmptyList[String],String :: Int :: shapeless.HNil] = Left(NonEmptyList(error 1, error 2))
可能值得打开一个 PR 来为 Kittens 添加类似的东西。
关于scala - 将其他列表转换为 HList 中的任一个,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59242860/
在学习无形时,我想知道为什么这不能编译: def someHList[H <: HList]: H = HNil 因为 HNil 对象扩展了扩展 HList 的 HNil 特性? 在返回一些 HLis
偶.head不起作用。 我需要做哪些改变才能使这项工作发挥作用? import shapeless._ import HList._ import Nat._ scala> case class Fo
我正在编写一个通用表格查看器,它应该能够显示任何类型的 Seq[Row]其中 Row B.sort(hlistNthLens[R,nat(ci)]))(tab.hdrs(ci).toString))
鉴于以下 case class A(value:Int) case class B(value:String) val h:Option[A] :: A :: Option[B] :: Option[
我正在寻找一种将两个 HList 压缩在一起的方法。第一个是从以其通用表示形式转换的案例类生成的,第二个是手动定义为 Nat 的 HList。 因此,我期望一个元组(或 2 成员 HList)包含案例
假设我有一个案例类: case class Foo(num: Int, str: String, bool: Boolean) 现在我还有一个简单的包装器: sealed trait Wrapper[
我正在学习 shapeless,目前我正在尝试创建一个执行以下操作的函数:给定一个 HList 类型,它返回 None 的 HList,Option 类型对应给定 HList 类型。 例如: crea
我想创建相当于: def toTupleN[A1, ..., AN, L <: HList](l: L): TupleN[A1, ..., AN] 代码使用 toTupleN只有当只有一个 N 时才应
这个问题来源于我之前的问题:What does HList#foldLeft() return? 我有这个场景: class Cursor { } trait Column[T] { def r
例如,如果我有一个 HList: HList>> list = ... 有没有办法将每个元素应用于柯里化(Currying)函数: F>> f = ... 这样我就能以某种方式得到 D ? 此外,如果
我正在尝试实现获取第一个元素的通用函数: import shapeless.ops.hlist.IsHCons import shapeless.{Generic, HList} object App
假设我们有这个: def join[A, B](a: A, b: B) def join[A, B, C](a: A, b: B, c: C) // etc 基本上有很多用于最多 20 个类型参数的重
我正在尝试以下类(class) import shapeless._ import syntax.std.tuple._ class TestedClass[HL](nodes: HL) {
下面的代码似乎很明显可以编译和运行 case class Pair(a: String, b: Int) val pairGen = Generic[Pair] object size extends
我有一个复杂的类型层次结构,但要分解它有两个基本特征:Convertable和 Conversion[A conv.convert(automaton)) } override def conv
假设我有一个像这样的类型类: trait Select[A] { def select(selector: String): Set[A] } typeclass 提供了“给定一个选择器字符串,给
我想做这样的事情: def run(subjects: List[Subject]) = { val configs = compute() subjects.map(s => configs
我一直在尝试使用 HList创建记录。 我一直在使用 HList-GHCSyntax 中定义的运算符. 到目前为止它工作得很好,让我可以写这样的东西: myRecord = (param1 .=.
HList package是基于现在古老的 Haskell 技术。一个简单的问题是:考虑到过去 8 年 Haskell/GHC 开发的所有精彩新特性,“现代”HList 的构建方式会非常不同吗?我意识
我试图解决 this problem与无形。但是由于某种原因我无法在 HList 上映射.我会让代码不言自明。 import shapeless._ import HList._ case class
我是一名优秀的程序员,十分优秀!