2017-05-05 81 views
1

我有以下應用性的定義和實現:哪裏其他結果

import simulacrum._ 

@typeclass trait Applicative[F[_]] { 

    def pure[A](a: A): F[A] 

    def apply[A, B](fa: F[A])(ff: F[A => B]): F[B] 

    def map[A,B](fa: F[A])(f: A => B): F[B] = 
    apply(fa)(pure(f)) 

} 

object Applicative { 

    implicit val optionApplicative: Applicative[Option] = new Applicative[Option] { 
    def pure[A](a: A): Option[A] = Some(a) 

    def apply[A, B](fa: Option[A])(ff: Option[A => B]): Option[B] = (fa, ff) match { 
     case (None, _) => None 
     case (Some(_), None) => None 
     case (Some(a), Some(f)) => Some(f(a)) 
    } 

    } 

    implicit val listApplicative: Applicative[List] = new Applicative[List] { 
    def pure[A](a: A): List[A] = List(a) 

    def apply[A, B](fa: List[A])(ff: List[A => B]): List[B] = 
     (fa zip ff).map { case (a, f) => f(a) } 

    } 

} 

當我名單上執行地圖:

Applicative[List].map(List(1,2,3))(_ + 1) 

我有:

res2: List[Int] = List(2) 

但我確實期待List(2,3,4)。我究竟做錯了什麼?

回答

4
def apply[A, B](fa: List[A])(ff: List[A => B]): List[B] = 
    (fa zip ff).map { case (a, f) => f(a) } 

這是由您ff大小是造成1 通過def pure[A](a: A): List[A] = List(a)

所以:

(fa zip ff).map { case (a, f) => f(a) } 

=>:

List(1, 2, 3) zip List(f).map { case (a, f) => f(a) } 

=>:

List((1, f)).map { case (a, f) => f(a) } 
0

要添加一個解決方案@ chengpohi的答案,我想指出有是ListApplicative實例的兩種經典方式。

第一個是你已經實現的Zip,所以當你有一個值列表和一個函數列表時,它適用於函數元素,一個接一個(第一個函數應用於第一個元素,等等上)。從大小爲nm的兩個列表開始,輸出將是大小爲min(n, m)的列表。

第二個是Cartesian,對於元素列表和函數列表,它將每個函數應用於每個元素。因此,如果您從大小爲nm的列表開始,您會得到大小爲n × m的列表。

下面是一個簡單的實現它:

implicit val cartesian: Applicative[List] = new Applicative[List] { 
    def pure[A](a: A): List[A] = List(a) 

    def apply[A, B](fa: List[A])(ff: List[A => B]): List[B] = for { 
    a <- fa 
    f <- ff 
    } yield f(a) 
} 

這第二個是比較標準(它確實是從Monad實例繼承了一個),並在貓scalaz AFAIK默認。

你一定要問自己,當你在兩個清單上使用你的應用程序時(你的期望是什麼)(大小大於1,有一個更好的想法)。你在這個問題上說了什麼,讓我覺得你最好不要在第二個問題上。請記住,這兩個是古典但不是唯一可能的。如果你需要一個自定義實例,你可以定義它(請記住它應該遵守Applicative法則)。