は、コードスニペットです: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
にcreateBackend
によって返さIBackend
をラップしていない場合は、createBackend
関数はコンパイルされません。しかし、今私はtest
のケースステートメントを使用してBackend
からunbox IBackend
の関数を使用する必要があります。ちょっと面倒です。 test
またはcreateBackend
の機能を改善するためのご意見はありますか?
'createBackend'と' test'は、引数として文字列を取る必要がありますか?可能ならば、タイプレベルであっても必要なバックエンドを識別するシングルトンのGADT型 'SBackend b 'を使うようにして、存在するラッパー' Backend'はもはや必要なくなります。これは、ユースケースによっては可能かもしれません。 – chi