如何处理Haskell中同一类的返回值



以下是代码段:

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts          #-}
{-# LANGUAGE LambdaCase                #-}
{-# LANGUAGE MultiParamTypeClasses     #-}
{-# LANGUAGE TypeFamilies              #-}
module Main
where
import           Control.Exception
import           System.IO
main :: IO ()
main = putStrLn "Hello World"
class IConn a where
  execute :: a -> IO ()
  delete :: a -> IO ()
data ConnA = ConnA
instance IConn ConnA where
  execute _ = putStrLn "Execute A"
  delete _ = putStrLn "Delete A"
data ConnB = ConnB
instance IConn ConnB where
  execute _ = putStrLn "Execute B"
  delete _ = putStrLn "Delete B"
class IConn (Conn b) => IBackend b where
  type Conn b :: *
  create :: b -> IO (Conn b)
  withConn :: b -> Int -> Int -> (Conn b -> IO a) -> IO a
  withConn b l u f = do
    putStrLn $ "low: " ++ show l
    putStrLn $ "up: " ++ show u
    bracket (create b) delete f
data BackendA = BackendA
data BackendB = BackendB
instance IBackend BackendA where
  type Conn BackendA = ConnA
  create _ = return ConnA
instance IBackend BackendB where
  type Conn BackendB = ConnB
  create _ = return ConnB
data Backend = forall b. IBackend b => Backend b
func :: IConn c => c -> IO ()
func c = do
  putStrLn "Beginning of func."
  execute c
  putStrLn "end of func."
createBackend :: String -> IO Backend
createBackend "A" = return $ Backend BackendA
createBackend "B" = return $ Backend BackendB
test :: String -> IO ()
test name =
  createBackend name >>= case
  Backend imp
    -> withConn imp 10 100 func

如果我不包装createBackend在数据Backend中返回的IBackend,则createBackend函数将不会编译。但是现在,我必须使用test函数中的案例语句从Backend卸下IBackend。有点麻烦。提高testcreateBackend功能的任何建议?

改进的一种方法是制作IBackend的CC_11实例。那么test功能将是:

test name = do
    imp <- createBackend name
    withConn imp 10 100 func

好吧,让我们尝试实例化:

instance IBackend Backend where
    type Conn Backend = ?
    create (Backend imp) = create imp
    withConn (Backend imp) = withConn imp

,但是我们对关联类型的Conn有问题。解决其建立连接的存在类型的一种方法。

data WrapConn = forall c . IConn c => WrapConn c
instance IConn WrapConn where
    execute (WrapConn c) = execute c
    delete (WrapConn c) = delete c
instance IBackend Backend where
    type Conn Backend = WrapConn
    create (Backend imp) = WrapConn <$> create imp
    withConn (Backend imp) x y f = withConn imp x y (f . WrapConn)

另一种方法是了解您的功能createBackend将在添加新的后端时更新。正如@chi所写,您可以通过Gadt Witch替换后端名称类型,代表使用构造的参数的后端类型,例如:

data Backend imp where
    ImpA :: Backend BackendA
    ImpB :: Backend BackendB
    ImpC :: OptionsC -> Backend BackendB
    ...
 createBackend :: Backend imp -> IO imp
 createBackend ImpA = return BackendA
 createBackend ImpB = return BackendB
 createBackend (ImpC options) = ...
 ...
 test :: IBackend imp => Backend imp -> IO ()
 test b = do
     imp <- createBackend b
     withConn imp 10 100 func

最新更新