Scalaz用于简化功能代码

时间:2016-07-09 13:25:52

标签: scala scalaz

我使用bing操作在Scalaz上写了一个Fibonacci函数。这是我的代码:

import scalaz._, Scalaz._

def fib(i: Int): Option[Int] = i match {
  case 0 => 0.some
  case 1 => 1.some
  case x if x > 1 =>
    fib(i - 1) >>= {
      a => fib(i - 2) >>= {
        b => (a + b).some
      }
    }
  case _ => None
}

是否可以简化它?我不喜欢这样的东西a => fib(i - 2) >>= ...

2 个答案:

答案 0 :(得分:5)

这看起来更简单,但没有Scalaz:

<-

基本上它是Haskell的记号,在这里你可以看到>>=flatMap(bind)的更方便的版本。实际上它通过编译器扩展到Future

当您需要为更复杂的类型执行此操作时,Scalaz可以提供帮助(让我们想象您希望将其设置为异步/有效,但仍然有序) - 您可以使用monadic变换器。以下是来自cat文档的解释(与scalaz非常相似,但更加清晰):http://typelevel.org/cats/tut/optiont.html

当事情变得更复杂时(假设您需要管理Taskdef minus(a: Int, b: Int): Task[Int] = Task.delay(a - b) def plus(a: Int, b: Int): Task[Int] = Task.delay(a + b) def fib(i: Int): Task[Option[Int]] = {...} 的有效计算以及错误处理),可能有助于使用高级变换器,例如: https://github.com/djspiewak/emm

P.S。 Fibonacci我们需要它吗?可能不是,但我们仍然可以设想一个外部服务,用于&#34;减去&#34;和#34;加&#34;,所以在这种情况下,Scalaz / Cats会提供帮助。

def minus(a: Int, b: Int): OptionT[Task, String] = OptionT(Task.delay(Some(a - b)))
def plus(a: Int, b: Int): OptionT[Task, String] = OptionT(Task.delay(Some(a - b)))

哪些scalaz /猫可以简化(至少绑定自己:)):

def minus(a: Int, b: Int): OptionT[Task, String] = OptionT.liftF(Task.delay(a - b))
def plus(a: Int, b: Int): OptionT[Task, String] = OptionT.liftF(Task.delay(a - b))

def fib(i: Int): OptionT[Task, String] = i match {
  case 0 => OptionT.pure(0)//OptionT.fromOption(0.some)
  case 1 => OptionT.pure(1)//OptionT.fromOption(1.some)
  case x if x > 1 => for {
    a <- fib(minus(i, 1))
    b <- fib(minus(i, 2))
    sum <- plus(a, b)
  } yield sum
  case _ => None
}

然后到:

Task

错误处理示例(scalaz / fs2 Throwable基本上封装了它们,但我们要说你需要精确的类型而不是type ServiceError = Throwable //use Xor.catchNonFatal to catch errors from external service def minus(a: Int, b: Int): Task[Xor[ServiceError, Int]] = Task.delay((a - b).right) def plus(a: Int, b: Int): Task[Xor[ServiceError, Int]] = Task.delay((a + b).right) def fib(i: Int): Task[Option[Xor[ServiceError, Int]]] = {...}

type ServiceError = Throwable //use Xor.catchNonFatal to catch errors from external service

//Note: you need kind-projector for that syntax: https://github.com/non/kind-projector
type E = Task |: (ServiceError \/ ?) |: Option |: Base 

def minus(a: Int, b: Int): Emm[E, Int] = ...
def plus(a: Int, b: Int): Emm[E, Int] = ...
def fib(i: Int): Emm[E, Int] = {...}

Emm会将其简化为:

def fib(i: Int): Option[Int] = i match {
  case x if x < 0 => None //explicit validation comes first
  case x if x > 1 => for {
    a <- fib(i - 1)
    b <- fib(i - 2)
  } yield a + b
  case x => x.some
}

您可以尝试完成这些示例作为练习。

P.S.2

您还可以简化模式匹配:

Xor

您可以尝试使用\/ / Free[Function0, A]作为练习进行验证:http://typelevel.org/cats/tut/xor.html

PS3请注意,所有实现都不是堆栈安全的(没有tailrec优化),因此您可能需要Trampoline来补偿(取决于您的实际任务,因为fibonacci以堆栈安全的方式实现起来要容易得多,但是在负数上没有错误,你基本上可以抛出异常或返回0):

http://blog.richdougherty.com/2009/04/tail-calls-tailrec-and-trampolines.html

Scalaz:http://eed3si9n.com/learning-scalaz/Stackless+Scala+with+Free+Monads.html

在猫蹦床只是Task:         https://github.com/typelevel/cats/blob/master/free/src/main/scala/cats/free/Trampoline.scala

谈到OptionT[Task, Int],它实际上是在内部蹦蹦跳跳,所以将它用于此类计算是安全的 - 所以monadic变换器(#import "ViewController.h" @interface ViewController () { NSMutableArray *arrayLabelKey; NSMutableArray *arrayLabelValue; int lblYHeight; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. arrayLabelKey = [[NSMutableArray alloc]init]; arrayLabelValue = [[NSMutableArray alloc]init]; lblYHeight = 200; //Intially I set label Y position is 200 } for(int i=0;i<array.count;i++) { for(NSString *strKey in [[array objectAtIndex:i]allKeys]) { [arrayLabelKey addObject:[self checkEmpty:strKey]]; //check strKey is empty or not with below method,because if string null or lenght=0 it crashes here.So that I call that below method.Now it does not crash. } for(NSString *strValue in [[array objectAtIndex:i]allValues]) { [arrayLabelValue addObject:[self checkEmpty:strValue]]; //check strValue is empty or not with below method,,because if string null or lenght=0 it crashes here.So that I call that below method.Now it does not crash. } } )将在这里出现。

答案 1 :(得分:1)

补充 dk14 的回答:

由于fib(i - 2)不依赖于fib(i - 1),我们实际上并不需要>>= / bind / flatMap,我们可以{ {1}}。

你的嵌套Applicative(或理解的等价物)可以替换为:

>>=

使用应用程序构建器语法甚至更简单:

Apply[Option].apply2(fib(i - 1), fib(i - 2))(_ + _)