1

저는 인공 생명 실험을위한 프레임 워크를 개발 중입니다. 프레임 워크 은 각 종이 Agent 클래스의 인스턴스 인 경우 여러 종을 지원할 수 있습니다. AgentBox에서 각 에이전트를 래핑하여 읽을 수 있고 쓰고 기본 유형을 모른 채 사용할 수 있습니다.하스켈 클래스의 일부가 아닌 상용구 피하기

이 방법은 잘 작동하지만, 사용자가 사용자가 작성해야하는 하나의 작은 상용구 기능이 있습니다. 나는 이것을 피하는 방법이 있는지 알고 싶습니다. 이것을 피하십시오. 함수의 형식 시그니처에 형식 변수가 없으므로 에이전트 클래스에서 해당 함수의 기본 구현을 제공 할 수 없습니다. 나는 상용구로 살 수 있지만, 더 좋은 방법이 있다면 을 알기 란 매우 궁금합니다.

다음은 내가 말하는 것에 대한 최소 작동 예제입니다. 매우 마지막에 getLock 함수는 내 사용자가 쓰기를 피하고 싶습니다. 에이전트 클래스의 모든 인스턴스는 이 에이전트를 읽고 상자에서 랩핑하는 함수를 제공해야하며, 구현은 항상 과 정확히 일치합니다.

{-# LANGUAGE ExistentialQuantification, DeriveGeneriC#-} 

import qualified Data.Serialize as DS (Get, Serialize, get, put) 
import Data.Serialize.Put (PutM) 
import Data.List (find) 
import Data.Maybe (fromJust, isNothing) 
import GHC.Generics (Generic) 

class Agent a where 
    agentId :: a -> String 
    speciesId :: a -> String 
    -- other functions to be added 

-- This wrapper allows me to use Agents without knowing their type. 
data AgentBox = forall a. (DS.Serialize a, Agent a) => AgentBox a 

-- Instructions for deserialising an agent 
data ReaderSpec = ReaderSpec { tag :: String, getter :: DS.Get AgentBox } 

-- Serialise an AgentBox by putting the species tag, then the agent. 
putAgentBox :: AgentBox -> PutM() 
putAgentBox (AgentBox a) = do 
    DS.put $ speciesId a 
    DS.put a 

-- Deserialise an agent by getting the species tag, looking up the getter 
-- for that species of agent, and then getting the agent itself. 
getAgentBox :: [ReaderSpec] -> DS.Get (Either String AgentBox) 
getAgentBox xs = do 
    s <- DS.get :: DS.Get String 
    let a = find (\x -> tag x == s) xs 
    if isNothing a 
    then return $ Left $ "No getter found for " ++ s 
    else do 
     let d = (getter . fromJust) a 
     t <- d 
     return $ Right t 

-- 
-- Everything above this line is provided by the framework. 
-- The user of the framework would create their own instances of the class 
-- Agent, by writing something like this: 
-- 

data Rock = Rock String Int deriving (Show, Generic) 

rockTag :: String 
rockTag = "Rock" 

readerSpec :: ReaderSpec 
readerSpec = ReaderSpec rockTag getRock 

instance Agent Rock where 
    agentId (Rock name _) = name 
    speciesId _ = rockTag 
    -- other functions to be added 

instance DS.Serialize Rock 

-- | Get the agent and wrap it in a box. 
getRock :: DS.Get AgentBox 
getRock = do 
    t <- DS.get :: DS.Get Rock 
    return $ AgentBox t 

답변

5

당신은 거의과 같이, 특정 유형 a에 대한 ReaderSpec의를 생성하는 기능을 쓸 수 있습니다 :

-- Create a 'ReaderSpec' that deserializes objects of type 'a' 
mkReaderSpec :: (DS.Serialize a, Agent a) => String -> ReaderSpec 

a 때문에이 매개 변수에 표시 또는 유형을 반환하지 않습니다, 그것은 통과하는 것이 필요 추가 매개 변수로서 유형에 대한 프록시. 일반적으로 이는 정의되지 않은 값을 전달하여 수행됩니다. 표현식은 asTypeOf으로 전화하여 유형 a이되어야합니다.

-- Create a 'ReaderSpec' that deserializes objects of type 'a' 
mkReaderSpec :: (DS.Serialize a, Agent a) => String -> a -> ReaderSpec 
mkReaderSpec tag dummy = ReaderSpec tag getter 
    where 
    getter = do {t <- DS.get; return $ AgentBox (t `asTypeOf` dummy)} 

이제 프레임 워크는 특정 유형의 ReaderSpec들 수 있습니다. 사용자는 undefined을 전달하여 유형 및 연관된 클래스 인스턴스를 선택합니다.

readerSpec = mkReaderSpec "Rock" (undefined :: Rock) 
관련 문제