gpt4 book ai didi

haskell - 使用 monad 堆栈进行依赖注入(inject)

转载 作者:行者123 更新时间:2023-12-04 15:30:30 24 4
gpt4 key购买 nike

我是 trying different approaches做有时被称为依赖注入(inject)的事情。为此,我详细说明了一个天气应用程序的简单示例,我们想要获取天气数据(从网络服务或硬件设备),存储天气数据(可以是数据库或只是一个文件),并报告(要么打印到屏幕上,要么说出天气)。这个想法是编写一个使用一些 fetch 的程序。 , store , 和 report功能,其实现可能会有所不同。

我已经成功地使用 functions 将关注点从检索、存储和报告的实现中分离出来并抽象出来。和 free-monads ,但是我用 monad 堆栈达到的解决方案看起来很糟糕:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module WeatherReporterMTL where

import Control.Monad.IO.Class
import Control.Monad.Trans.Class

type WeatherData = String

class Monad m => WeatherService m where
fetch :: m WeatherData

class Monad m => Storage m where
store :: WeatherData -> m ()

class Monad m => Reporter m where
report :: WeatherData -> m ()

-- | A dummy implementation of the @WeatherService@
newtype DummyService m a = DummyService { runDummyService :: m a }
deriving (Functor, Applicative, Monad, MonadIO)

instance MonadIO m => WeatherService (DummyService m) where
fetch = return "won't get any warmer in December."

-- | A dummy implementation of the @Storage@
newtype DummyStorage m a = DummyStorage { runDummyStorage :: m a }
deriving (Functor, Applicative, Monad, MonadIO, WeatherService)

-- It seems wrong that the storage has to be an instance the weather service
-- (@WeatherService@) ...

instance MonadIO m => Storage (DummyStorage m) where
store d = liftIO $ putStrLn $ "No room left for this report: " ++ d

-- | A dummy implementation of the @Reporter@
newtype DummyReporter m a = DummyReporter { runDummyReporter :: m a }
deriving (Functor, Applicative, Monad, MonadIO, WeatherService, Storage)

-- Ok, now this seems even worse: we're putting information about
-- how we're gonna stack our monads :/

instance MonadIO m => Reporter (DummyReporter m) where
report d = liftIO $ putStrLn $ "Here at the MTL side " ++ d

reportWeather :: (WeatherService m, Storage m, Reporter m) => m ()
reportWeather = do
w <- fetch
store w
report w

dummyWeatherReport :: IO ()
dummyWeatherReport = runDummyService $ runDummyStorage $ runDummyReporter reportWeather

在上面的代码中, DummyStorageDummyReporter必须有 WeatherService 的琐碎实例,这显然是错误的。此外,这些实例取决于 monad 最终堆叠的顺序。有没有办法避免在不同堆栈之间泄漏信息?

最佳答案

与其将实现绑定(bind)到特定的新类型,也许您可​​以拥有需要访问 IO 和一些必要的簿记状态的“自由 float ”实现函数,例如

data WeatherState = WeatherState -- dummy
fetch' :: (MonadState WeatherState m,MonadIO m) => m WeatherData
fetch' = undefined
data StorageState = StorageState -- dummy
store' :: (MonadState StorageState m,MonadIO m) => WeatherData -> m ()
store' = undefined
data ReporterState = ReporterState -- dummy
report' :: (MonadState ReporterState m,MonadIO m) => WeatherData -> m ()
report' = undefined

“注入(inject)”意味着在 StateT 上创建一些新类型。携带所需的状态,然后声明实例
newtype Injected a = 
Injected { getInjected :: StateT (WeatherState,StorageState,ReportState) a }
deriving (Functor,Applicative,Monad)

instance WeatherService Injected where
fetch = Injected $ zoom _1 fetch'

instance Storage Injected where
store x = Injected $ zoom _2 $ store' x

instance Reporter Injected where
report x = Injected $ zoom _3 $ report' x

( _1 来自 microlens, zoom 来自 microlens-mtl。)

关于haskell - 使用 monad 堆栈进行依赖注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47579216/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com