2017-05-04 41 views
0

這裏的代碼片段:如何處理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 

如果我不包裝在數據Backend返回由createBackendIBackend,該createBackend功能將無法編譯。但現在我必須使用test函數中的case語句從Backend取消箱IBackend。這有點麻煩。任何改善testcreateBackend功能的建議?

+1

做'createBackend'和'test'必須採取一個字符串作爲參數?如果可能的話,如果可能的話,更好的辦法是讓它們採用一個單一的GADT類型'SBackend b'來標識想要的後端,即使在類型級別,這樣就不再需要存在的包裝器'Backend'。根據您的使用情況,這可能是不可能的。 – chi

回答

0

改進的一種方法是如果您製作BackendIBackend的實例。然後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女巫後端名稱類型將代表後端的類型與結構參數,就像這樣:

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 
相關問題