2012-07-20 83 views
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作为什么时实际使用的占位符)

我有一个函数updateConfig,它将WorkConfig作为它的一个参数。

问题是我想能够强制使用哪个构造函数。 例如在函数phaseOne中,我希望能够保证当调用updateConfig时,只能使用PhaseTwo_wc构造函数。

为了使用一种类型的类此的执法,我将不得不作出独立的数据构造,例如:

data PhaseOne_wc = PhaseOne_wc BuildConfig Filename (Maybe XMLFilepath) 

如果我走这条路,我还有一个问题要解决。我有其他的数据类型有WorkConfig作为一个值,我会做什么来解决这个问题?例如,

type ConfigTracker = TMVar (Map CurrentPhase WorkConfig) 

如何使用类型系统进行强制执行我想要,同时牢记上面提到的内容?

ConfigTracker必须能够知道我想要的数据类型。

*澄清: 我正在寻求限制哪个WorkConfig updateConfig可能作为参数。

+0

当调用'updateConfig'时,构造函数被“使用”的含义并不十分清楚。你是否想限制什么样的'WorkConfig'可以作为参数传递给*'updateConfig'?或者保证'updateConfig' *返回由相应构造函数构成的'WorkConfig'? – Wyzard 2012-07-20 00:39:09

+0

更新了澄清 – 2012-07-20 00:51:38

+0

也许一个typeclass会更适合您的需求? – alternative 2012-07-20 00:52:02

回答

1

为了更具体和推动讨论,这个GADT有多接近存在代码达到你想要的?

{-# 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)) 
+0

这非常接近。我想我可以用这个来得到我想要的。谢谢 ! – 2012-07-20 17:59:00

2

你的问题有点含糊,所以我会在一般的答案。

如果你有一个类型的形式为:

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

...你想要一些功能f能够在所有三个构造原理:

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

...但你想要一些函数g只适用于最后一个构造函数,那么你有两个选择。

第一个选项是创建嵌入C3第二类型:

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) 

...并gSecondType功能:

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

这只代码稍微复杂的f,你必须先解开C3访问C4构造。

第二个解决方案是,你只是让g存储在C3构造函数值的函数:

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

这不需要修改fMyType类型。

0

对不起,这是一个比答案更多的扩展评论。我有点困惑。这听起来像你有一些功能,如

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

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

和你想确保例如,phaseOne的定义中,这永远不会出现:

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

但现在我问你:是updateConfig纯粹的(非单子)功能?因为如果是这样,比phaseOne可以很容易地完全正确,并且仍然用PhaseTwo_wc调用updateConfig;也就是说,它可能只是扔掉结果(即使它是一元太):

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

换句话说,我想知道,如果你想强制约束真的是你正在寻找的实际属性?但是现在,如果我们想到单子,有一种共同的模式,你所描述的是类似于:制造“特殊”monad,限制可以执行的动作的种类;例如

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

instance Monad PhaseOneMonad where ... 

这可能就是你所得到的吗?还请注意PhaseOneUpdate不需要WorkConfig;它只需要它感兴趣的构造函数参数。这是另一种常见模式:不能约束使用哪个构造函数,但可以直接使用参数。

嗯...仍然不确定。