2012-02-25 14 views
2

接続を受け入れると、新しいスレッドがフォークされ、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)に実装することは可能ですか?

ありがとうございます!

+0

'cdSocket'と' handleMessage'のクラスコンテキストは冗長です。 'ClientData a'はすべてのクラスメソッドのコンテキスト内に暗黙的にあります。 – Heatsink

答えて

0

私はここでタイプクラスを使用するとは思わない。あなたが作業したい小規模な限定されたケースのセットを持っている場合は、代数データ型を使用するほうがはるかに簡単です。

例えば、

data ClientData = ClientData { cdSocket :: Socket, cdName :: Maybe Text } 

またはあなたがMaybeより明示的な何か、

type Name = Text 
data UserInfo = Unregistered | Customer Name 
data ClientData = ClientData { cdSocket :: Socket, cdUserInfo :: UserInfo } 

をしたい場合は、その後、ちょうど別のケースを処理するためにパターンマッチングを使用することができます。

handleMessage clientData msg = 
    case cdUserInfo clientData of 
    Unregistered -> -- implementation 
    Customer name -> -- implementation