接続を受け入れると、新しいスレッドがフォークされ、initializeClient clientSocketという簡単なTCPサービスがあるとします。ハスケル型クラスと複数のファイル
initializeClientは次のようになります。簡単にするために
initializeClient clientSocket = do
clientDataTVar <- atomically $ newTVar (UnregisteredClientData clientSocket)
_ <- forkIO $ clientSocketReadLoop clientDataTVar
、のはclientDataTVarで唯一の2つの可能なインスタンスがあると仮定してみましょう:
data UnregisteredClientData = UnregisteredClientData {
ucdSocket :: Socket
}
data CustomerClientData = CustomerClientData {
ccdSocket :: Socket,
ccdName :: Text
}
未登録のクライアントが自分の名前を含むメッセージを送信し、そのClientDataTVar値は置き換えられます:
writeTVar unregisteredClientDataTVar $ CustomerClientData clientSocket theirName
を編集する:TVarのタイプがTVar UnregisteredClientDataであるため、上記のwriteTVarラインはタイプチェックされません。 CustomerClientDataを書き込むことはできませんが、私が達成しようとしていることを説明するためにここに残しています。
この時点で、コマンドのハンドラが戻り、clientSocketReadLoopが再び実行されます。
clientSocketReadLoopはソケットにしか興味あるので、私は次のことを行うことができます:
今class ClientData a where
cdSocket :: a -> Socket
instance ClientData UnregisteredClientData where
cdSocket = ucdSocket
instance ClientData CustomerClientData where
cdSocket = ccdSocket
、clientSocketReadLoopはclientDataTVarを読み込むとき、それは関係なく、基になる型の、cdSocket clientDataを呼び出し、ソケットを取得することができますUnregisteredClientDataまたはCustomerClientDataです。 recvを実行した後、ハンドラを呼び出す必要があります。どのハンドラを呼び出すかは、ユーザのタイプ(未登録または顧客)によって異なります。
私は、これはクラスとインスタンスへのhandleMessageを添加することによって達成することができると信じて:
class ClientData a where
cdSocket :: a -> Socket
handleMessage :: a -> String -> IO()
instance ClientData UnregisteredClientData where
cdSocket = ucdSocket
handleMessage unregisteredClientDataTVar msg = (implementation)
instance ClientData CustomerClientData where
cdSocket = ccdSocket
handleMessage customerClientDataTVar msg = (implementation)
したがって、適切なハンドラが呼び出されます。
私が知りたいのですがどのようなは次のようになります。これはそれを行うには良い方法
ですか?私はそれがかなり標準的なシナリオだと思いますが、私はClientDataのためにTVarを使いますが、他の人はソケットを渡すだけかもしれません。これはあまり変わってはいけません。
クラス/インスタンス定義を1つのファイル(Types.hs)に入れ、ハンドラを別のファイル(Client.hs)に実装することは可能ですか?
ありがとうございます!
'cdSocket'と' handleMessage'のクラスコンテキストは冗長です。 'ClientData a'はすべてのクラスメソッドのコンテキスト内に暗黙的にあります。 – Heatsink