如何定义函数类型,它们将是monadic,IO和pure

jos*_*uan 1 haskell

(相关问题在运行时选择实例行为)

我想定义一种后端(可重用为独立api),然后提供多个实现并能够在运行时选择一个.

我的申请就像

data AppData =
     AppData { ...
             , backend :: Backend
             }
Run Code Online (Sandbox Code Playgroud)

为简单起见,让我们的后端(概念代码)

data Backend key value =
     Backend { get :: [(key, value)]
             , cud :: key -> Maybe value -> () -- Create Update Delete
             }
Run Code Online (Sandbox Code Playgroud)

现在定义我们Backend类型的正确/推荐方法是什么?

我认为这将是一元(当时Monad m),而且IO(当时MonadIO m),而且纯(那么我们需要改变-> ()-> Backend key value)等等...

我怀疑,下一次尝试是monadic,IO纯粹,但可能是过度工程

data Backend m k v =
     Backend { get :: MonadIO m => m [(k, v)]
             , cud :: MonadIO m => k -> Maybe v -> Backend m k v
             }
Run Code Online (Sandbox Code Playgroud)

MonadIO是一个强大的限制和返回不可变版本cud是多余的(在大多数情况下?)与m.

抽象它的正确/推荐方法是什么?

谢谢!

一旦定义了Backend我的API,我的意图或多或少被用作(概念代码)

main = do
    configuration <- getAppConfiguration
    selectedBackend <- case preferedBackend configuration of
                           "mongoDB"  -> MongoDBBackend.makeBackend
                           "sqlite"   -> SqliteBackend.makeBackend
                           "volatile" -> PureDataMapBackend.makeBackend
                           ...
    appData <- makeAppData configuration selectedBackend
    ....
Run Code Online (Sandbox Code Playgroud)

Pet*_*lák 7

如果您还需要非IO后端,那么我建议通过运行其运算的monad对数据类型进行参数化.正如@Cactus所指出的,不需要为数据类型本身添加约束.它没有任何帮助,只是让事情变得复杂.相反,这些约束将在创建各种Backends的函数处.

此外,尽管可能会改变更新函数的返回类型,但使用这样的函数将只是地狱,因为应用程序基本上需要在使用函数时涵盖所有(两个)情况.所以相反,我建议保持结果简单,只是monadic.对于纯后端,您可以在Statemonad中运行应用程序,例如:

{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.State

data Backend m key value = Backend
    { beGet :: m [(key, value)]
    , beCud :: key -> Maybe value -> m () -- Create Update Delete
    }

pureBackend :: (MonadState [(k, v)] m, Eq k) => Backend m k v
pureBackend = Backend get pureCud
  where
    filterOut k = filter ((/= k) . fst)
    pureCud k Nothing = modify (filterOut k)
    pureCud k (Just v) = modify (((k, v) :) . filterOut k)

-- other backends ...
Run Code Online (Sandbox Code Playgroud)

这意味着AppData其他人使用的东西也Backend需要由monad进行参数化.