Ada*_*ser 7 scala option case-class
我是Scala的新手,我仍然习惯于习惯语法和风格,所以这可能是一个非常简单的问题.
我正在使用一个代码库,其中有很多用Options填充的case类,如下所示:
case class Person(
pants: Option[Pants]
)
case class Pants(
pocket: Option[Pocket]
)
case class Pocket(
cash: Option[Cash]
)
case class Cash(
value: String = "zilch"
)
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,你将如何去恢复多少钱是在Person的Pants Pocket,如果他们确实穿开裆裤......口袋,如果他们有任何的钱呢?
Cha*_*ton 12
Scalaz 7已经改变了一点,所以这是另一个例子:
object PartialLensExample extends App {
import scalaz._
import Lens._
import PLens._
case class Bar(blub: Option[String])
case class Foo(bar: Option[Bar])
// normal lenses for getting and setting values
val fooBarL: Foo @> Option[Bar] = lensg(foo ? bar ? foo.copy(bar = bar), _.bar)
val barBlubL: Bar @> Option[String] = lensg(bar ? blub ? bar.copy(blub = blub), _.blub)
// compose the above as 'Partial Lenses', >=> is just an alias for 'andThen'
val fooBarBlubL: Foo @?> String = fooBarL.partial >=> somePLens >=> barBlubL.partial >=> somePLens
// try it
val foo = Foo(Some(Bar(Some("Hi"))))
println(fooBarBlubL.get(foo)) // Some(Hi)
println(fooBarBlubL.set(foo, "Bye")) //Foo(Some(Bar(Some(Bye))))
// setting values
val foo2 = Foo(None)
println(fooBarL.set(foo2, Some(Bar(None)))) // Foo(Some(Bar(None)))
}
Run Code Online (Sandbox Code Playgroud)
理解的好时机:
val someCash: Option[Cash] =
for( pants <- somePerson.pants;
pocket <- pants.pocket;
cash <- pocket.cash ) yield cash
Run Code Online (Sandbox Code Playgroud)
等效地,您可以编写以下内容,其中第一个代码是语法糖(忽略一些细微之处):
val someCash: Option[Cash] =
somePerson.pants.flatMap(_.pocket.flatMap(_.cash))
Run Code Online (Sandbox Code Playgroud)
(我不完全确定你是否可以使用_通配符编写最后一个表达式,就像我做的那样).
问题没有提到修改数据,但是当你需要这样做时,你很快发现Scala库没有工具来简化这些(当数据是不可变的时).如果你还没有经历过这样的呢,试着写一个函数,它会替换或修改,该value的Cash持有一个Person,使用问题定义的类型.
如Tony Morris 在Scala中的非对称镜头所述,镜头是解决这个问题的合适解决方案.
以下是我们如何使用Scalaz的scalaz-seven分支和(部分镜头)实现来访问和更新value某个人的示例.CashLensPLens
首先,一些样板:为案例类的每个字段定义Lens实例.A @-@ B意思是一样的Lens[A, B].
val pants: Person @-@ Option[Pants] =
lensG(_.pants, p => ps => p.copy(pants = ps))
val pocket: Pants @-@ Option[Pocket] =
lensG(_.pocket, ps => p => ps.copy(pocket = p))
val cash: Pocket @-@ Option[Cash] =
lensG(_.cash, p => c => p.copy(cash = c))
val value: Cash @-@ String =
lensG(_.value, c => v => c.copy(value = v))
Run Code Online (Sandbox Code Playgroud)
然而,我们无法构成所有这些镜头,因为大多数领域都是包装Option类型.
拯救的部分镜头:这些允许我们访问和更新可能不存在的结构的部分,例如a的Some值Option或heada的值List.
我们可以使用somePLensScalaz 7 的功能创建一个部分镜头,查看每个可选字段.然而,为了用我们的常规镜头构成部分镜头,我们需要使用partial每个镜头上的方法来获取普通镜头的等效部分镜头实例Lens.
// @-? is an infix type alias for PLens
val someCash: Pocket @-? Cash = cash.partial andThen somePLens
scala> someCash.get(Pocket(Some(Cash("zilch"))))
res1: Option[Cash] = Some(Cash(zilch))
Run Code Online (Sandbox Code Playgroud)
以同样的方式,我们可以创建我们的部分镜头,Person通过组合我们所有镜头的partial实例来查看所持有的现金,并将实例夹在中间somePLens.在这里,我使用了<=<运算符,一个别名andThen(相当于compose切换操作数).
val someCashValue: Person @-? String =
pants.partial <=< somePLens <=<
pocket.partial <=< somePLens <=<
cash.partial <=< somePLens <=<
value.partial
Run Code Online (Sandbox Code Playgroud)
创建要使用的Person实例:
val ben = Person(Some(Pants(Some(Pocket(Some(Cash("zilch")))))))
Run Code Online (Sandbox Code Playgroud)
使用部分镜头获取现金的价值我有:
scala> someCashValue.get(ben)
res2: Option[String] = Some(zilch)
Run Code Online (Sandbox Code Playgroud)
使用部分镜头修改值:
scala> someCashValue.mod(_ + ", zero, nada", ben)
res3: Person = Person(Some(Pants(Some(Pocket(Some(Cash(zilch, zero, nada)))))))
Run Code Online (Sandbox Code Playgroud)
现在,如果我不(!)穿任何裤子,我们可以看到修改我的现金价值的尝试如何将没有任何效果:
scala> val ben = Person(None)
ben: Person = Person(None)
scala> someCashValue.mod(_ + ", zero, nada", ben)
res4: Person = Person(None)
Run Code Online (Sandbox Code Playgroud)