저는 인공 생명 실험을위한 프레임 워크를 개발 중입니다. 프레임 워크 은 각 종이 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