日常使用的Scalaz功能第1部分:Typeclasses和Scala扩展

你们中的大多数人都可能听说过一本很棒的Javascript书:Javascript的精髓。 出于同样的考虑,我想展示一些Scalaz的东西,这些东西在日常项目中确实非常有用,而不必深入(至少对我来说)Scalaz令人恐惧的内部工作原理。 在第一部分中,我们将深入探讨许多有用的类型类。 在以后的部分中,我们将介绍诸如Monad Transformers,Free monad,Validation等内容。

对于这些示例,我们将使用scala REPL。 因此,如果您想沿启动scala并加载scala库,请执行以下操作:

scala> :require /Users/jos/.ivy2/cache/org.scalaz/scalaz-core_2.11/bundles/scalaz-core_2.11-7.2.1.jar
Added '/Users/jos/.ivy2/cache/org.scalaz/scalaz-core_2.11/bundles/scalaz-core_2.11-7.2.1.jar' to classpath.
 
scala> import scalaz._
import scalaz._
 
scala> import Scalaz._
import Scalaz._

在第一篇文章中,我们将研究Scalaz库中的以下类型类:

  1. 等于 typeclass:用于typesafe等于运算符。
  2. 订单类型类:用于更安全的类型订购
  3. 枚举类型类:创建功能丰富的枚举

除此之外,我们还将研究Scalaz对标准Scala库提供的一些类型进行的一些简单扩展。 我们不会看Scalaz添加的所有内容,而只会看一下OptionBoolean的几个扩展。

有用的类型类

使用类型类,您可以轻松地向现有类添加功能(请参阅Pimp我的库模式 )。 Scalaz附带了一些有用的类型类,您可以立即使用它们。

Typesafe等于运算符

Scalaz提供了类型安全的equals运算符,当比较无效类型时,该运算符将引发编译错误。 因此,尽管在Scala中使用==!= ,您可以使用Scalaz ==== / =运算符比较StringInt会导致编译时错误:

scala> 1 == 1
res6: Boolean = true
scala> 1 === 1
res7: Boolean = true
scala> 1 == "1"
res8: Boolean = false
scala> 1 === "1"
<console>:14: error: type mismatch;
 found   : String("1")
 required: Int
              1 === "1"

Scalaz提供了以下一组运算符,这些行为可以从函数实现中轻松看出。

final def ===(other: F): Boolean = F.equal(self, other)
final def /==(other: F): Boolean = !F.equal(self, other)
final def =/=(other: F): Boolean = /==(other)
final def ≟(other: F): Boolean = F.equal(self, other)
final def ≠(other: F): Boolean = !F.equal(self, other)
订单类型类别

这是一个非常简单的类型类,提供了更多类型安全的排序。 就像使用Equals运算符一样,我们现在可以在编译时捕获两种不同类型的比较:

scala> 1 < 4d
res25: Boolean = true
 
scala> 1 lte 4d
<console>:14: error: type mismatch;
 found   : Double(4.0)
 required: Int
              1 lte 4d
 
scala> 1 ?|? 1
res31: scalaz.Ordering = EQ
 
scala> 1 ?|? 2
res32: scalaz.Ordering = LT
 
scala> 1 ?|? 2d
<console>:14: error: type mismatch;
 found   : Double(2.0)
 required: Int
              1 ?|? 2d

Scalaz为此提供了以下一组运算符:

final def <(other: F): Boolean = F.lessThan(self, other)
final def <=(other: F): Boolean = F.lessThanOrEqual(self, other)
final def >(other: F): Boolean = F.greaterThan(self, other)
final def >=(other: F): Boolean = F.greaterThanOrEqual(self, other)
final def max(other: F): F = F.max(self, other)
final def min(other: F): F = F.min(self, other)
final def cmp(other: F): Ordering = F.order(self, other)
final def ?|?(other: F): Ordering = F.order(self, other)
final def lte(other: F): Boolean = F.lessThanOrEqual(self, other)
final def gte(other: F): Boolean = F.greaterThanOrEqual(self, other)
final def lt(other: F): Boolean = F.lessThan(self, other)
final def gt(other: F): Boolean = F.greaterThan(self, other)
枚举类型类

使用Scalaz Enum类型,创建枚举器非常容易,其功能比标准Scala或Java库中的枚举器更多。 它提供了许多遍历枚举的功能,并有助于创建新的子集。 Scalaz提供以下功能集:

final def succ: F = F succ self
final def -+-(n: Int): F = F.succn(n, self)
final def succx: Option[F] = F.succx.apply(self)
final def pred: F = F pred self
final def ---(n: Int): F = F.predn(n, self)
final def predx: Option[F] = F.predx.apply(self)
final def from: EphemeralStream[F] = F.from(self)
final def fromStep(step: Int): EphemeralStream[F] = F.fromStep(step, self)
final def |=>(to: F): EphemeralStream[F] = F.fromTo(self, to)
final def |->(to: F): List[F] = F.fromToL(self, to)
final def |==>(step: Int, to: F): EphemeralStream[F] = F.fromStepTo(step, self, to)
final def |-->(step: Int, to: F): List[F] = F.fromStepToL(step, self, to)

可以在stackoverflow上找到一个非常好的示例: http : //stackoverflow.com/questions/28589022/enumeration-concept-in-scala-which-option-to-take ,但是这需要进行一些小的更改才能全部Scalaz好东西。 以下代码显示了如何使用此枚举:

scala> import scalaz.Ordering._
import scalaz.Ordering._
 
scala> :paste
// Entering paste mode (ctrl-D to finish)
 
  case class Coloring(val toInt: Int, val name: String)
 
  object Coloring extends ColoringInstances {
 
    val RED = Coloring(1, "RED")
    val BLUE = Coloring(1, "BLUE")
    val GREEN = Coloring(1, "GREEN")
  }
 
  sealed abstract class ColoringInstances {
 
    import Coloring._
 
    implicit val coloringInstance: Enum[Coloring] with Show[Coloring] = new Enum[Coloring] with Show[Coloring] {
 
      def order(a1: Coloring, a2: Coloring): Ordering = (a1, a2) match {
        case (RED, RED) => EQ
        case (RED, BLUE | GREEN) => LT
        case (BLUE, BLUE) => EQ
        case (BLUE, GREEN) => LT
        case (BLUE, RED) => GT
        case (GREEN, RED) => GT
        case (GREEN, BLUE) => GT
        case (GREEN, GREEN) => EQ
      }
 
      def append(c1: Coloring, c2: => Coloring): Coloring = c1 match {
        case Coloring.RED => c2
        case o => o
      }
 
      override def shows(c: Coloring) = c.name
 
      def zero: Coloring = Coloring.RED
 
      def succ(c: Coloring) = c match {
        case Coloring.RED => Coloring.BLUE
        case Coloring.BLUE => Coloring.GREEN
        case Coloring.GREEN => Coloring.RED
      }
 
      def pred(c: Coloring) = c match {
        case Coloring.GREEN => Coloring.BLUE
        case Coloring.BLUE => Coloring.RED
        case Coloring.RED => Coloring.GREEN
      }
 
      override def max = Some(GREEN)
 
      override def min = Some(RED)
 
    }
  }
 
// Exiting paste mode, now interpreting.
 
defined class Coloring
defined object Coloring
defined class ColoringInstances

现在,我们可以使用Scalaz枚举操作中定义的所有功能:

scala> import Coloring._
import Coloring._
 
scala> RED
res0: Coloring = Coloring(1,RED)
 
scala> GREEN
res1: Coloring = Coloring(1,GREEN)
 
scala> RED |-> GREEN
res2: List[Coloring] = List(Coloring(1,RED), Coloring(1,BLUE), Coloring(1,GREEN))
 
scala> RED succ
warning: there was one feature warning; re-run with -feature for details
res3: Coloring = Coloring(1,BLUE)
 
scala> RED -+- 1
res4: Coloring = Coloring(1,BLUE)
 
scala> RED -+- 2
res5: Coloring = Coloring(1,GREEN)

不错吧? 这是创建灵活且功能丰富的枚举的绝佳方法。

标准类扩展

就像我们在本文开头所说的那样,我们将看看Scalaz如何通过向其标准类中添加一些功能来使标准Scala库具有更多的功能。

选项带来更多乐趣

使用Optional类型类,Scalaz使得使用Scala Options更加容易。 例如,它提供使构造更容易的功能:

scala> Some(10)
res11: Some[Int] = Some(10)
 
scala> None
res12: None.type = None
 
scala> some(10)
res13: Option[Int] = Some(10)
 
scala> none[Int]
res14: Option[Int] = None

您会看到,这些函数的结果类型是Option [T]而不是Some或None。 您可能想知道为什么这样做有用,但是请看以下内容:假设我们有一个选项列表,我们要对其进行折叠:

scala> val l = List(Some(10), Some(20), None, Some(30))
l: List[Option[Int]] = List(Some(10), Some(20), None, Some(30))
 
scala> l.foldLeft(None) { (el, z) => el.orElse(z)  }
<console>:22: error: type mismatch;
 found   : Option[Int]
 required: None.type
              l.foldLeft(None) { (el, z) => el.orElse(z)  }

这将失败,因为我们的折叠期望结果为None.type而不是Option。 当我们使用Scalaz版本时,它可以按预期工作:

scala> l.foldLeft(none[Int]) { (el, z) => el.orElse(z)  }
res19: Option[Int] = Some(10)

如果不引入一些新的运营商,Scalaz就不会成为Scalaz。

// Alternative for getOrElse
scala> Some(10) | 20
res29: Int = 10
 
scala> none | 10
res30: Int = 10
 
// Ternary operator
scala> Some(10) ? 5 | 4
res31: Int = 5
 
// ~ operator: Returns the item contained in the Option if it is defined, otherwise, the zero element for the type A
scala> some(List())
res32: Option[List[Nothing]] = Some(List())
 
scala> ~res32
res33: List[Nothing] = List()
 
scala> some(List(10))
res34: Option[List[Int]] = Some(List(10))
 
scala> ~res34
res35: List[Int] = List(10)

没什么复杂的,只是一些辅助功能。 Scalaz库中的Options周围还有许多其他内容,但这超出了本文的范围。

更多布尔功能

Scalaz还为布尔类型添加了一些功能。

# Ternary operations are back!
scala> true ? "This is true" | "This is false"
res45: String = This is true
 
scala> false ? "This is true" | "This is false"
res46: String = This is false
 
# Returns the given argument if this is `true`, otherwise, the zero element for the type of the given argument.
scala> false ?? List(120,20321)
res55: List[Int] = List()
 
scala> true ?? List(120,20321)
res56: List[Int] = List(120, 20321)

以及用于二进制算术的其他运算符的完整列表:

// Conjunction. (AND)
final def ∧(q: => Boolean) = b.conjunction(self, q)
// Conjunction. (AND)
final def /\(q: => Boolean) = ∧(q)
// Disjunction. (OR)
final def ∨(q: => Boolean): Boolean = b.disjunction(self, q)
// Disjunction. (OR)
final def \/(q: => Boolean): Boolean = ∨(q)
// Negation of Disjunction. (NOR)
final def !||(q: => Boolean) = b.nor(self, q)
// Negation of Conjunction. (NAND)
final def !&&(q: => Boolean) = b.nand(self, q)
// Conditional.
final def -->(q: => Boolean) = b.conditional(self, q)
// Inverse Conditional.
final def <--(q: => Boolean) = b.inverseConditional(self, q)
// Bi-Conditional.
final def <-->(q: => Boolean) = b.conditional(self, q) && b.inverseConditional(self, q)
// Inverse Conditional.
final def ⇐(q: => Boolean) = b.inverseConditional(self, q)
// Negation of Conditional.
final def ⇏(q: => Boolean) = b.negConditional(self, q)
// Negation of Conditional.
final def -/>(q: => Boolean) = b.negConditional(self, q)
// Negation of Inverse Conditional.
final def ⇍(q: => Boolean) = b.negInverseConditional(self, q)
// Negation of Inverse Conditional.
final def <\-(q: => Boolean) = b.negInverseConditional(self, q)

例如:

scala> true /\ true
res57: Boolean = true
 
scala> true /\ false
res58: Boolean = false
 
scala> true !&& false
res59: Boolean = true
有关其他功能的更多信息

在这篇简短的文章中,我们仅向您展示了Scalaz提供的几个附加功能。 如果您想了解更多信息,最简单的方法就是查找源,在这种情况下,它可以提供非常有用的信息,例如:

  • scalaz.syntax.std.BooleanOps
  • scalaz.syntax.std.ListOps
  • scalaz.syntax.std.MapOps
  • scalaz.syntax.std.OptionOps
  • scalaz.syntax.std.StringOps

一些例子:

列出乐趣
# get the tail as an option
scala> List(10,20,30)
res60: List[Int] = List(10, 20, 30)
 
scala> res60.tailOption
res61: Option[List[Int]] = Some(List(20, 30))
 
scala> List()
res64: List[Nothing] = List()
 
scala> res64.tailOption
res65: Option[List[Nothing]] = None
 
# intersperse the list with additional elements
scala> List(10,20,30)
res66: List[Int] = List(10, 20, 30)
 
scala> res66.intersperse(1)
res68: List[Int] = List(10, 1, 20, 1, 30)
 
# from list to List[List] of all possibilities
scala> List('a','b','c','d').powerset
res71: List[List[Char]] = List(List(a, b, c, d), List(a, b, c), List(a, b, d), List(a, b), List(a, c, d), List(a, c), List(a, d), List(a), List(b, c, d), List(b, c), List(b, d), List(b), List(c, d), List(c), List(d), List())
地图乐趣
# alter one entry in a safe manner
res77: scala.collection.immutable.Map[Char,Int] = Map(a -> 10, b -> 20)
scala> res77.alter('a')(f => f |+| some(5))
res78: Map[Char,Int] = Map(a -> 15, b -> 20)
 
# intersect two maps, and determine which value to keep of the keys that intersect
scala> val m1 =  Map('a' -> 100, 'b' -> 200, 'c' -> 300)
m1: scala.collection.immutable.Map[Char,Int] = Map(a -> 100, b -> 200, c -> 300)
 
scala> val m2 = Map('b' -> 2000, 'c' -> 3000, 'd' -> 4000)
m2: scala.collection.immutable.Map[Char,Int] = Map(b -> 2000, c -> 3000, d -> 4000)
 
scala> m1.intersectWith(m2)((m1v,m2v) => m2v)
res23: Map[Char,Int] = Map(b -> 2000, c -> 3000)
 
scala> m1.intersectWith(m2)((m1v,m2v) => m1v)
res24: Map[Char,Int] = Map(b -> 200, c -> 300)
弦乐
# Make a string plural (in a somewhat naive way)
scala> "Typeclass".plural(1)
res26: String = Typeclass
 
scala> "Typeclass".plural(2)
res27: String = Typeclasss
 
scala> "Day".plural(2)
res28: String = Days
 
scala> "Weekly".plural(2)
res29: String = Weeklies
 
# safely parse booleans, bytes, shorts, longs, floats, double and ints
scala> "10".parseDouble
res30: scalaz.Validation[NumberFormatException,Double] = Success(10.0)
 
scala> "ten".parseDouble
res31: scalaz.Validation[NumberFormatException,Double] = Failure(java.lang.NumberFormatException: For input string: "ten")

结论

我希望您喜欢Scalaz的第一个简短介绍。 正如您所看到的那样,这些简单的功能已经提供了很多附加值,而无需深入研究Scalaz的非常复杂的内部工作原理。 这里使用的模式只是一个简单的TypeClass模式,用于将功能添加到某些标准Scala功能中。

在下一篇文章中,我们将在研究Monad Transformers时看一些更复杂的功能。

翻译自: https://www.javacodegeeks.com/2016/04/scalaz-features-everyday-usage-part-1-typeclasses-scala-extensions.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值