[英]Tagless-final effect propagation
無標記最終模式讓我們編寫純函數程序,明確它們所需的效果。
但是,擴展此模式可能會變得具有挑戰性 我將嘗試用一個例子來證明這一點。 想象一個簡單的程序,它從數據庫中讀取記錄並將它們打印到控制台。 我們將需要一些自定義類型類Database
和Console
,以及來自cats / scalaz的Monad
以構成它們:
def main[F[_]: Monad: Console: Database]: F[Unit] =
read[F].flatMap(Console[F].print)
def read[F[_]: Functor: Database]: F[List[String]] =
Database[F].read.map(_.map(recordToString))
當我想為內層中的函數添加新效果時,問題就開始了。 例如,如果沒有找到記錄,我希望我的read
功能記錄消息
def read[F[_]: Monad: Database: Logger]: F[List[String]] =
Database[F].read.flatMap {
case Nil => Logger[F].log("no records found") *> Nil.pure
case records => records.map(recordToString).pure
}
但現在,我必須將Logger
約束添加到read
鏈的所有調用者。 在這個人為的例子中,它只是main
,但想象這是一個復雜的現實世界應用程序的幾個層次。
我們可以通過兩種方式來看待這個問題:
main
不關心日志記錄,它只需要read
的結果。 此外,在實際應用中,您會在頂層看到很長的效果鏈。 這感覺就像一個代碼味道,但我無法指出我能采取的其他方法。 很想得到你對此的見解。
謝謝。
我們也可以說這泄漏了實現細節 - 主要不關心日志記錄,它只需要讀取的結果。 此外,在實際應用中,您會在頂層看到很長的效果鏈。 這感覺就像一個代碼味道,但我無法指出我能采取的其他方法。
我實際上相信相反的是真的。 純FP的一個關鍵承諾是等式推理,作為從它的簽名中導出方法實現的一種手段。 如果read
需要記錄效果才能完成業務,那么無論如何都應該在簽名中以聲明方式表達。 明確你的影響的另一個好處是,當它們開始積累時,或許我們需要重新考慮這個特定方法正在做什么並將其拆分成更小的組件? 或者這種效果真的應該在這里使用嗎?
確實,效果會疊加,但正如評論中提到的@TravisBrown一樣,它通常是調用堆棧中最高的位置,必須“承受”實際上為整個調用樹提供所有隱式證據的后果。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.