2015-09-24 19 views
20

我正在學習Scala中的免費monad,並且我已經組合了一個簡單的代數示例,可以使用貓將其提升爲免費monad。在Scala中堆疊免費Monad中的單點特效

這裏是我的代數

sealed trait ConsultationOp[A] 
object consultation { 
    case class Create(c: Consultation) extends ConsultationOp[Unit] 
    case class Get(s: ConsultationId) extends ConsultationOp[Option[Consultation]] 
} 

而且我能夠使用它等,其中隱式執行從ConsultationOpFree提升

def app = for { 
    c <- consultation.Create(Consultation("123", "A consultation")) 
    _ <- consultation.Get(c._id) 
} yield() 

def interpreters = ConsultationInterpreter or UserInterpreter 
app.foldMap(interpreters) 

(還有很多細節丟失,全面執行工作是在這裏:https://github.com/gabro/free-api

到目前爲止好,但如果我需要提取由consultation.Get返回的可選值什麼。

,想到的第一件事是一個單子轉換,即像

def app = for { 
    c <- consultation.Create(Consultation("123", "A consultation")).liftM[OptionT] 
    d <- OptionT(consultation.Get(c._id)) 
    _ <- doSomethingAConsultation(d) 
} yield() 

,但它看起來醜陋,但感覺不對。

什麼是榮耀的方式 - 如果有的話 - 堆疊一元效應的使用免費單子什麼時候?

+1

有關這[這裏]的討論(https://www.reddit.com/r/scala/comments/5p3fc3/free_monads_in_scala_web_stack_part_i/dco5yqy/)。要點是使用Free不會讓你無法處理'ConsultationOp'中的值'A'。像[freek](https://github.com/ProjectSeptemberInc/freek)和[eff](https://github.com/atnos-org/eff)這樣的庫可以更加優雅地解決這個問題。 –

回答

3

我看到在這些情況下,經常性的常用方法是使用遍歷,所以你可以沿着線的更改代碼:

import cats.syntax.traverse._ 
import cats.instances.option._ 

// ... 

def app = for { 
    c <- consultation.Create(Consultation("123", "A consultation")) 
    d <- consultation.Get(c._id) 
    _ <- d.traverseU(doSomethingAConsultation(_)) 
} yield() 

其中,恕我直言,是比單子變壓器替代乾淨多了。 請注意,您可能需要一些其他的import稍微修改代碼,我沒有嘗試,但概念是:利用導線。

+0

謝謝!這是常見做法(根據您的經驗),還是將monad堆棧移動到解釋器並在那裏從'ConsultationOp'自然轉換到(例如)'Future [Option]'可能更常見? –

+0

在我的,小的說實話,經驗我總是用'遍歷'來解決這類問題。然而,將monad堆棧移動到解釋器是一種選擇,並且一些庫可以做到這一點,例如,自由泳:https://github.com/47deg/freestyle/blob/master/freestyle-effects/shared/src/main/scala/effects/option.scala。不幸的是,我還沒有時間仔細觀察它。 – lambdista

+0

謝謝!這就說得通了。 –