2012-12-11 15 views
7

我打算在我的Scala代碼中開始使用Monadic風格,以用於線程狀態等。下面是合成3一元函數(僅約副作用關心)的一個簡單的例子Scalaz - 將列表和狀態Monad結合在一起以便理解

import scalaz._ 
import Scalaz._ 

object MonadTest { 
    def adder(i: Int) = State[String, Int] ({str: String => (str + i.toString + " ", i) }) 
    val oneTwoThreeMonad = for { 
    m1 <- adder(1) 
    m2 <- adder(2) 
    m3 <- adder(3) 
    } yield m3 
    oneTwoThreeMonad("start: ")._1 //String = "start: 1 2 3 " 
} 

這都是不言自明和按預期工作。但是,對於這種方法對我來說真的很有用,我希望能夠將其與List合併。這裏有一點(不工作)的代碼來說明我的意思:

val list = List(1, 2, 3) 

val oneTwoThreeBis = for { 
    i <- list 
    mx <- adder(i) 
} yield mx 

基本上,我想能夠基於從List參數單子結合 - 在每個元素的運行一元函數list並隨着我去累積副作用。我知道示例語法不起作用,我明白爲什麼它不 - 我只是尋找一個乾淨,優雅的等價物。

我非常確定,使用斯卡拉茲單體變壓器可以實現這一點,更具體地說,與StateT,但我真的不知道如何去做這件事。

PS。我使用的是Scalaz 7.0-M3,所以語法可能與最常見的6.x有些不同。

回答

9

我不知道我明白你在尋找什麼,但它聽起來像是你想要的東西更像traverse這裏(其中traverse是Haskell的mapM的更一般的版本):

import scalaz._, Scalaz._ 

def adder(i: Int) = State[String, Int](str => (str + i.toString + " ", i)) 

List(1, 2, 3).traverseS(adder)("start: ")._1 

這將打印以下,符合市場預期:

res0: String = "start: 1 2 3 " 

請注意,我使用traverseS(其中S代表State),以避免不得不WRI剔除相當雜亂的類型參數,但在任何時候想要將monadic函數映射到可遍歷的東西上時,traverse更爲普遍。

我很高興給出一個StateT的例子,如果這不是你想要的,但這將最終與你有List[(String, Int)]類型的東西。

+0

看起來像我在找什麼。但是這並沒有改變我將不得不學會在實踐中早晚使用狀態轉換器的事實) – nietaki

+1

我剛剛意識到我已經閱讀了你的答案之一,其中包含了'traverse'和'traverseS'的詳細描述甚至爲此讚美你。我無法想象它是如何不跟我在一起的... – nietaki

+1

如果有人需要做類似貓的事情而不是斯卡拉斯,請注意沒有'traverseS',但我有一個使用'traverseU'的工作示例:https://github.com/benhutchison/gesture/blob/master/core/jvm/src/test/scala/gesture/GestureProcessorSpec.scala#L34 –