2016-03-03 10 views
5

私はScalaで独自の理解可能な互換性のあるモナドとファンクタを実装したいと考えています。独自の理解力のあるスカラーモナドを作る方法は?

例として2つの愚かなモナドを取ってみましょう。 1つのモナドは、マップまたはフラット・マップできる "Int"を含む状態モナドです。

val maybe = IntMonad(5) 
maybe flatMap(a => 3 * (a map (() => 2 * a))) 
// returns IntMonad(30) 

別のモナドがかかるので、のような関数合成は...

val func = FunctionMonad(() => println("foo")) 
val fooBar = func map (() => println("bar")) 
fooBar() 
// foo 
// bar 
// returns Unit 

の例では、いくつかのミスがあるかもしれませんが、あなたのアイデアを得るん。

私はこの2つの異なるタイプのMonadsをScalaでの理解のために使用したいと考えています。このように:

val myMonad = IntMonad(5) 
for { 
    a <- myMonad 
    b <- a*2 
    c <- IntMonad(b*2) 
} yield c  
// returns IntMonad(20) 

私はScalaのマスターではないけど、あなたは、理解の範囲内で使用するタイプのアイデア

答えて

9

を取得し、あなたは本当にそれだけのためにmapflatMapメソッドを定義する必要があります同じ型のインスタンスを返します。構文的には、for-comprehensionは、コンパイラによって一連のflatMapに変換され、続いてyieldの最終mapに変換されます。これらのメソッドが適切な署名で利用可能である限り、それは動作します。

私はあなたの例で後にしている本当にわからないんだけど、ここでOptionと等価である簡単な例です:

sealed trait MaybeInt { 
    def map(f: Int => Int): MaybeInt 
    def flatMap(f: Int => MaybeInt): MaybeInt 
} 

case class SomeInt(i: Int) extends MaybeInt { 
    def map(f: Int => Int): MaybeInt = SomeInt(f(i)) 
    def flatMap(f: Int => MaybeInt): MaybeInt = f(i) 
} 

case object NoInt extends MaybeInt { 
    def map(f: Int => Int): MaybeInt = NoInt 
    def flatMap(f: Int => MaybeInt): MaybeInt = NoInt 
} 

私は(二つのサブタイプと共通の特徴を持っている私は、しかし、私が望むほど多くを持つことができました)。共通の特性MaybeIntは、各サブタイプがmap/flatMapインタフェースに適合するように強制します。

scala> val maybe = SomeInt(1) 
maybe: SomeInt = SomeInt(1) 

scala> val no = NoInt 
no: NoInt.type = NoInt 

for { 
    a <- maybe 
    b <- no 
} yield a + b 

res10: MaybeInt = NoInt 

for { 
    a <- maybe 
    b <- maybe 
} yield a + b 

res12: MaybeInt = SomeInt(2) 

また、あなたはforeachfilterを追加することができます。あなたも、この(なし収量)を処理する場合:

for(a <- maybe) println(a) 

をあなたはforeachを追加します。あなたはifガード使用する場合と:

for(a <- maybe if a > 2) yield a 

をあなたは filterまたは withFilterが必要になります。

フル例:

sealed trait MaybeInt { self => 
    def map(f: Int => Int): MaybeInt 
    def flatMap(f: Int => MaybeInt): MaybeInt 
    def filter(f: Int => Boolean): MaybeInt 
    def foreach[U](f: Int => U): Unit 
    def withFilter(p: Int => Boolean): WithFilter = new WithFilter(p) 

    // Based on Option#withFilter 
    class WithFilter(p: Int => Boolean) { 
     def map(f: Int => Int): MaybeInt = self filter p map f 
     def flatMap(f: Int => MaybeInt): MaybeInt = self filter p flatMap f 
     def foreach[U](f: Int => U): Unit = self filter p foreach f 
     def withFilter(q: Int => Boolean): WithFilter = new WithFilter(x => p(x) && q(x)) 
    } 
} 

case class SomeInt(i: Int) extends MaybeInt { 
    def map(f: Int => Int): MaybeInt = SomeInt(f(i)) 
    def flatMap(f: Int => MaybeInt): MaybeInt = f(i) 
    def filter(f: Int => Boolean): MaybeInt = if(f(i)) this else NoInt 
    def foreach[U](f: Int => U): Unit = f(i) 
} 

case object NoInt extends MaybeInt { 
    def map(f: Int => Int): MaybeInt = NoInt 
    def flatMap(f: Int => MaybeInt): MaybeInt = NoInt 
    def filter(f: Int => Boolean): MaybeInt = NoInt 
    def foreach[U](f: Int => U): Unit =() 
} 
+0

あなた自身を定義するのではなく、Scallazモナドの型を使用する方がよいでしょうか? –

+0

'filter' apiは、モナド項目を「解凍」する必要もあります。たとえば、MaybeIntに似ているが、 'caseクラスAB(a:Int、b:Int)'に対してMaybeABを使用する場合、フィルタは 'for(AB(a、b)< - maybAB)... ' –

関連する問題