2

ラッパーとして実存型を使用しています。囲まれた型を知っているコードのある時点で、囲まれた型に固有の何かを実行したいと思います。これは私が得ることができる最も近いものです:

 {-# LANGUAGE ExistentialQuantification #-}

class Agent a where
  agentId :: a -> String
  speciesId :: a -> String
  -- plus other functions that all agents support

-- | A wrapper allowing my daemon to read and write agents of any species.
--   (Agents are stored in files that contain a tag so I know which function
--   to call to read the agent.)
data AgentBox = forall a. Agent a => AgentBox { unbox :: a }

instance Agent AgentBox where
  agentId (AgentBox a) = agentId a
  speciesId (AgentBox a) = speciesId a
  -- plus other functions that all agents support

bugTag :: String
bugTag = "Bug"

data Bug = Bug String

instance Agent Bug where
  agentId (Bug name) = name
  speciesId _ = bugTag

doSomethingWith :: AgentBox -> IO ()
doSomethingWith a = do
  if speciesId a == bugTag
    then do
      -- Now I know it's a bug, and I want to do something bug-specific
      doBugStuff2 a
      return ()
    else return ()

doBugStuff :: Bug -> IO ()
doBugStuff a = putStrLn $ agentId a ++ " does bug stuff"

doBugStuff2 AgentBox{unbox=a} = doBugStuff (a `asTypeOf` model) -- line 39
  where model = undefined :: Bug

私が得るエラーは次のとおりです。

Amy30.hs:39:45:
    Could not deduce (a ~ Bug)
    from the context (Agent a)
      bound by a pattern with constructor
                 AgentBox :: forall a. Agent a => a -> AgentBox,
               in an equation for `doBugStuff2'
      at Amy30.hs:39:13-29
      `a' is a rigid type variable bound by
          a pattern with constructor
            AgentBox :: forall a. Agent a => a -> AgentBox,
          in an equation for `doBugStuff2'
          at Amy30.hs:39:13
    In the first argument of `asTypeOf', namely `a'
    In the first argument of `doBugStuff', namely
      `(a `asTypeOf` model)'
    In the expression: doBugStuff (a `asTypeOf` model)
Failed, modules loaded: none.

どうすればこれを達成できますか?よろしくお願いします。

4

3 に答える 3

8

Data.Dynamicを使用します。

import Data.Dynamic

class Typeable a => Agent a where
  agentId :: a -> String
  -- no need for speciesId

fromAgentBox :: Agent a => AgentBox -> Maybe a
fromAgentBox (AgentBox inner) = fromDynamic (toDyn inner)

instance Agent Bug where
  agentId (Bug name) = name
  -- no need for speciesId

doSomethingWith :: AgentBox -> IO ()
doSomethingWith a = do
  case fromAgentBox a of
    Just bug -> do
      -- Now the compiler knows it's a bug, and I can do something bug-specific
      doBugStuff2 bug
      return ()
    Nothing -> return ()

doSomethingWithまたは、おそらくデフォルトの定義を使用して、Agentクラスで宣言することを検討してください。

class Agent a where
  agentId :: a -> String
  -- still don't need speciesId
  doSomethingWith :: a -> IO ()
  doSomethingWith _ = return ()

instance Agent Bug where
  agentId (Bug name) = name
  -- still don't need speciesId
  doSomethingWith bug = do
    -- Now the compiler knows it's a bug, and I can do something bug-specific
    doBugStuff2 bug
    return ()

AgentBox最後に、あなたの型は実存的な型クラスのアンチパターンの例であることを指摘しておく必要があります。そのため、上で書いたことを無視して、Agentクラスを通常のデータ型として再設計する必要があります。

于 2012-10-11T15:26:29.220 に答える
4

Could not deduce (a ~ Bug).

私たちはできますが、コンパイラはできません。これは単射であるため、異なる型の 2 つのインスタンスが同じString を持つこと
わかっていますが、コンパイラはそれを推測できません。関数を適用すると、について持っていた型情報がすべて失われます。また、存在修飾されていたため、あまり情報がありませんでした。agentIdagentIdAgent a -> Stringa

問題 1: 存在するデータ型により、コンパイラはデータの型を使用できなくなります。これがあなたの問題の核心です。それらを異なるタイプにしたいと決めた後、それらをすべて 1 つのタイプにしたいと決めました。

問題 2: 文字列は型ではなく、型です。文字列よりも優れているのは、ユーザー定義型です。

data Species = Bug | Saurapod | ....

しかし、データよりも優れているのは実際の型です。作成してから非表示にしないでください。

解決策 1:

存在型は避けてください。タイプ class を持つ代わりにAgent、レコード タイプを持ち、data Agentすべてのエージェントを統一します。

data Agent = Agent {
    agentId :: String,
    speciesId :: Species,
    -- ...other stuff agents need. 
    -- Species-specific data is an illusion; 
    -- make Agent widely useful, catering for the eventualities
    }

解決策 2:

存在型は避けてください。エージェントにインターフェースを提供する型クラスを持つ代わりに、必要なビットで構成されるデータ型を持ちます。

data Agent = Agent {
    agentId :: String,
    speciesId :: Species,
    -- ...other stuff _all_ agents need. 
    }

class IsAgent a where
  agent :: a -> Agent

今、あなたは持つことができます

agents::[Agent]
agents = map agent bugs 
      ++ map agent saurapods 
      ++ ...

解決策 3:

存在型は避けてください。存在するエージェントを持つ代わりに、エージェントのユニオン型を持つ

class Agent a where
   -- all the stuff you want
instance Agent Bug where
   ...
instance Agent Saurapod where
   ...
data AnyAgent = ABug Bug | ASaurapod Saurapod | ... 
   -- ensure you have an agent instance for each type you need

instance Agent AnyAgent where
   -- much boilerplate code unwrapping and applying

agents :: [AnyAgent]
agents = map ABug bugs ++ map ASaurapod saurapods ++ ....

解決策 4:

存在型は避けてください。存在するエージェントを持つ代わりに、一般的なエージェント コードを分離し、これを含むエージェントのユニオン タイプを持ちます。

data Agent = Agent {
    agentId :: String,
    -- ...other stuff _all_ agents need. 
    }

data Bug = Bug --..... Bug-specific data
data Saurapod = Saurapod --... Saurapod-specific data

data AnyAgent = ABug Agent Bug | ASaurapod Agent Saurapod | ... 

agent :: AnyAgent -> Agent
agent (ABug a _) = a
agent (ASaurapod a _) = a
...

agents :: [AnyAgent]
agents = [ABug (Agent {agentId="007", ...}) (Bug ....),
          ASaurapod (Agent {agentId="Pat", ...}) (Saurapod ....),
          ...]

解決策 5

存在型を放棄することを拒否し、静的型付けの楽しくて簡単なものを残すことを選択し、型情報を回復するためにまたは何か他の楽しくないものを使用しDynamicますTypable

于 2012-10-11T16:54:17.457 に答える
3

You have to convince the type checker as well that you have the type is Bug. You can do this by making Data.Typeable.Typeable a super-class of Agent and then use Data.Typeable.cast to downcast from the existential type to the actual type.

But before doing this, consider doing it some other way. This is not very Haskellish, but rather in OO style.

{-# LANGUAGE ExistentialQuantification, DeriveDataTypeable #-}
import Data.Typeable
import Data.Maybe

class Typeable a => Agent a where
  agentId :: a -> String
  speciesId :: a -> String

data AgentBox = forall a. Agent a => AgentBox { unbox :: a }
    deriving (Typeable)

instance Agent AgentBox where
  agentId (AgentBox a) = agentId a
  speciesId (AgentBox a) = speciesId a

bugTag :: String
bugTag = "Bug"

data Bug = Bug String
    deriving (Typeable)

instance Agent Bug where
  agentId (Bug name) = name
  speciesId _ = bugTag

doSomethingWith :: AgentBox -> IO ()
doSomethingWith a = do
  case cast a of
    Just bug -> doBugStuff bug
    Nothing -> return ()

doBugStuff :: Bug -> IO ()
doBugStuff a = putStrLn $ agentId a ++ " does bug stuff"
于 2012-10-11T15:29:29.713 に答える