1

問題:

現在、私はWorkConfigこのようなタイプを持っています

data WorkConfig = PhaseZero_wc BuildConfig
                | PhaseOne_wc BuildConfig Filename (Maybe XMLFilepath)
                | PhaseTwo_wc String
                | SoulSucker_wc String
                | ImageInjector_wc String
                | ESX_wc String
                | XVA_wc String
                | VNX_wc String
                | HyperV_wc String
                | Finish_wc String
                    deriving Show

String実際に使用されるもののプレースホルダーとしてPhaseTwo_wc以降を使用しています)

updateConfigWorkConfigをパラメータの1つとして受け取る関数があります。

問題は、どのコンストラクターを使用するかを強制できるようにしたいということです。たとえば、関数では、が呼び出されたときにコンストラクターのみを使用phaseOneできることを保証できるようにしたいと思います。updateConfigPhaseTwo_wc

この強制に型クラスを使用するには、次のように個別のデータコンストラクターを作成する必要があります。

 data PhaseOne_wc = PhaseOne_wc  BuildConfig Filename (Maybe XMLFilepath)

このルートに行くと、解決すべき別の問題があります。値として持つ他のデータ型がありWorkConfigますが、これに対処するにはどうすればよいですか?例えば、

type ConfigTracker = TMVar (Map CurrentPhase WorkConfig)

上記のことを念頭に置いて、希望する施行に型システムをどのように使用できますか?

ConfigTrackerは、必要なデータ型を認識できる必要があります。

*明確化:updateConfigがパラメーターとして使用できるWorkConfigを制限しようとしています。

4

3 に答える 3

2

あなたの質問は少し曖昧だったので、一般的に答えます。

フォームのタイプがある場合:

data MyType a b c d e f g = C1 a b | C2 c | C3 e f g

f...そして、3つのコンストラクターすべてで機能する関数が必要です。

f :: MyType a b c d e f g -> ...

...しかしg、最後のコンストラクターでのみ機能する関数が必要な場合は、2つの選択肢があります。

C3最初のオプションは、 :内に埋め込まれた2番目のタイプを作成することです。

data SecondType e f g = C4 e f g

...そしてそれを元のC3コンストラクターに埋め込みます:

data MyType a b c d e f g = C1 a b | C2 c | C3 (SecondType e f g)

...そして次gの関数を作成しますSecondType

g :: SecondType e f g -> ...

コンストラクターにアクセスするfには最初に解凍する必要があるため、これによりコードが少し複雑になります。C3C4

2番目の解決策は、コンストラクターgに格納されている値の関数を作成することです。C3

g :: e -> f -> g -> ...

fこれには、MyTypeタイプを変更する必要はありません。

于 2012-07-20T02:17:44.257 に答える
1

より具体的に議論を推進するために、このGADT&Existentialコードはあなたが望むものにどれだけ近づいていますか?

{-# LANGUAGE GADTs, KindSignatures, DeriveDataTypeable, ExistentialQuantification, ScopedTypeVariables, StandaloneDeriving #-}
module Main where

import qualified Data.Map as Map
import Data.Typeable
import Data.Maybe

data Phase0 deriving(Typeable)
data Phase1 deriving(Typeable)
data Phase2 deriving(Typeable)

data WC :: * -> * where
  Phase0_ :: Int -> WC Phase0
  Phase1_ :: Bool -> WC Phase1
 deriving (Typeable)
deriving instance Show (WC a)

data Some = forall a. Typeable a => Some (WC a)
deriving instance Show Some

things :: [Some]
things = [ Some (Phase0_ 6) , Some (Phase1_ True) ]

do'phase0 :: WC Phase0 -> WC Phase1
do'phase0 (Phase0_ i) = Phase1_ (even i)

-- Simplify by using TypeRep of the Phase* types as key
type M = Map.Map TypeRep Some

updateConfig :: forall a. Typeable a => WC a -> M -> M
updateConfig wc m = Map.insert key (Some wc) m
  where key = typeOf (undefined :: a)

getConfig :: forall a. Typeable a => M -> Maybe (WC a)
getConfig m = case Map.lookup key m of
                Nothing -> Nothing
                Just (Some wc) -> cast wc
   where key = typeOf (undefined :: a)

-- Specialization of updateConfig restricted to taking Phase0
updateConfig_0 :: WC Phase0 -> M -> M
updateConfig_0 = updateConfig

-- Example of processing from Phase0 to Phase1
process_0_1 :: WC Phase0 -> WC Phase1
process_0_1 (Phase0_ i) = (Phase1_ (even i))

main = do
  print things
  let p0 = Phase0_ 6
      m1 = updateConfig p0 Map.empty
      m2 = updateConfig (process_0_1 p0) m1
  print m2
  print (getConfig m2 :: Maybe (WC Phase0))
  print (getConfig m2 :: Maybe (WC Phase1))
  print (getConfig m2 :: Maybe (WC Phase2))
于 2012-07-20T02:17:14.310 に答える
0

申し訳ありませんが、これは回答というよりも拡張されたコメントです。私は少し混乱しています。あなたは次のようないくつかの機能を持っているようですね

phaseOne = ...
           ... (updateConfig ...) ...

phaseTwo = ...
           ... (updateConfig ...) ...

phaseOneそして、たとえば、の定義内で、これが決して表示されないことを確認しようとしています。

phaseOne = ...
           ... (updateConfig $ PhaseTwo_wc ...) ...

しかし今、私はあなたに尋ねます:updateConfig純粋な(非モナディック)関数ですか?もしそうなら、よりphaseOne簡単に完全に正しく、それでも;updateConfigで呼び出すことができるからです。PhaseTwo_wcつまり、結果を破棄する可能性があります(そして、それがモナディックであっても):

phaseOne = ...
           ... (updateConfig $ PhaseTwo_wc ...) `seq` ...

言い換えれば、あなたが強制しようとしている制約が本当にあなたが探している実際のプロパティであるかどうか疑問に思っていますか?

しかし今、私たちがモナドについて考えている場合、あなたが説明するものはある種のような一般的なパターンがあります。実行できるアクションの種類を制限する「特別な」モナドを作成します。例えば

data PhaseOneMonad a = PhaseOnePure a | PhaseOneUpdate BuildConfig Filename (Maybe XMLFilepath) a

instance Monad PhaseOneMonad where ...

これはおそらくあなたが得ているものですか?;PhaseOneUpdateを取らないことにも注意してください。WorkConfig関心のあるコンストラクターパラメーターを受け取るだけです。これは別の一般的なパターンです。使用するコンストラクターを制限することはできませんが、引数を直接取ることができます。

うーん...まだわかりません。

于 2012-07-20T02:23:56.403 に答える