如何调用可能会失败的构造函数,尤其是在实现' Read'时和'任意&#39 ;? [英] How do I call a constructor that may fail, especially when implementing 'Read' and 'Arbitrary'?
问题描述
我有一个公共保险柜",可能会因(可能提供信息的)错误而失败:
I have a "public safe" that may fail with a (potentially informative) errors:
data EnigmaError = BadRotors
| BadWindows
| MiscError String
instance Show EnigmaError where
show BadRotors = "Bad rotors"
show BadWindows = "Bad windows"
show (MiscError str) = str
configEnigma :: String -> String -> String -> String -> Except EnigmaError EnigmaConfig
configEnigma rots winds plug rngs = do
unless (and $ [(>=1),(<=26)] <*> rngs') (throwError BadRotors)
unless (and $ (`elem` letters) <$> winds') (throwError BadWindows)
-- ...
return EnigmaConfig {
components = components',
positions = zipWith (\w r -> (mod (numA0 w - r + 1) 26) + 1) winds' rngs',
rings = rngs'
}
where
rngs' = reverse $ (read <$> (splitOn "." $ "01." ++ rngs ++ ".01") :: [Int])
winds' = "A" ++ reverse winds ++ "A"
components' = reverse $ splitOn "-" $ rots ++ "-" ++ plug
但是我不清楚该怎么称呼,特别是(特别是)在实现 Read
和任意
(用于QuickCheck)中.
but it is unclear how I should call this, particularly (and specifically) in implementing Read
and Arbitrary
(for QuickCheck).
对于前者,我可以做到
instance Read EnigmaConfig where
readsPrec _ i = case runExcept (configEnigma c w s r) of
Right cfg -> [(cfg, "")]
Left err -> undefined
where [c, w, s, r] = words i
,但这似乎最终隐藏了 err
中可用的错误信息;而对于后者,我被困在
but this seems to end up hiding error information available in err
; while for the latter, I'm stuck at
instance Arbitrary EnigmaConfig where
arbitrary = do
nc <- choose (3,4) -- This could cover a wider range
ws <- replicateM nc capitals
cs <- replicateM nc (elements rotors)
uk <- elements reflectors
rs <- replicateM nc (choose (1,26))
return $ configEnigma (intercalate "-" (uk:cs))
ws
"UX.MO.KZ.AY.EF.PL" -- TBD - Generate plugboard and test <<<
(intercalate "." $ (printf "%02d") <$> (rs :: [Int]))
失败,其预期类型与实际类型不匹配:
which fails with a mismatch between the expected and actual types:
预期类型:Gen EnigmaConfig实际类型:Gen(transformers-0.4.2.0:Control.Monad.Trans.Except.Except Crypto.Enigma.EnigmaError EnigmaConfig)
Expected type: Gen EnigmaConfig Actual type: Gen (transformers-0.4.2.0:Control.Monad.Trans.Except.Except Crypto.Enigma.EnigmaError EnigmaConfig)
当(公共安全")构造函数可能会失败时,尤其是在为我的课程实现 Read
和 Arbitrary
时,该如何使用呢?>
How do I call a ("public safe") constructor when it may fail, particularly when using it in implementing Read
and Arbitrary
for my class?
推荐答案
Read
类型类将解析表示为成功列表(失败与没有成功相同);因此,您应该返回 []
,而不是 undefined
.至于丢失有关出了什么问题的信息:是的,并且 readsPrec
的类型意味着您不能为此做太多事情.如果您确实确实想要[注意:我不认为您应该这样做],则可以围绕 EnigmaError EnigmaConfig
以外的对象定义一个新类型包装器,并为它提供一个 Read
实例成功解析了配置错误.
The Read
typeclass represents parses as lists of successes (with failures being the same as no successes); so rather than undefined
you should return []
. As for losing information about what went wrong: that's true, and the type of readsPrec
means you can't do much about that. If you really, really wanted to [note: I don't think you should want this] you could define a newtype wrapper around Except EnigmaError EnigmaConfig
and give that a Read
instance that had successful parses of configuration errors.
对于任意
,您有两种选择.一种选择是所谓的拒绝采样.例如
For Arbitrary
you have a couple choices. One choice is so-called rejection sampling; e.g.
arbitrary = do
-- ...
case configEnigma ... of
Left err -> arbitrary -- try again
Right v -> return v
您还可以将任意
实例视为内部API的一部分,并使用不安全的内部调用而不是使用安全的公共API来构造您的配置.其他选项包括调用 error
或 fail
.(我认为这四个选项大致按优先顺序排列-拒绝采样,然后是不安全的内部调用,然后是 error
,然后是 fail
,尽管您的判断可能有所不同.)
You might also consider an Arbitrary
instance to be part of your internal API, and use unsafe, internal calls rather than using the safe, public API for constructing your configuration. Other options include calling error
or fail
. (I consider these four options to be in roughly preference order -- rejection sampling, then unsafe internal calls, then error
, then fail
-- though your judgement may differ.)
这篇关于如何调用可能会失败的构造函数,尤其是在实现&#39; Read&#39;时和&#39;任意&#39 ;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!