如何调用可能失败的构造函数,特别是在实现“读取”和“任意”时? [英] How do I call a constructor that may fail, especially when implementing 'Read' and 'Arbitrary'?
问题描述
数据EnigmaError = BadRotors
| BadWindows
| MiscError String
实例显示EnigmaError其中
显示BadRotors =坏转子
显示BadWindows =坏窗口
show(MiscError str)= str
configEnigma :: String - >字符串 - >字符串 - >字符串 - >除了EnigmaError EnigmaConfig
configEnigma rots winds plug rngs = do
unless(and $ [(> = 1),(< = 26)] * * rngs')(throwError BadRotors)
除非(和$(`elem`字母)< $> winds')(throwError BadWindows)
- ...
返回EnigmaConfig {
components = components'
位置= zipWith(\wr - >(mod(numA0 w - r + 1)26)+ 1)风'rngs',
rings = rngs'
}
rngs'= reverse $(read< $>(splitOn。$01.++ rngs ++.01):: [Int])
winds' =A++ reverse winds ++A
components'= reverse $ splitOn - $ rots ++ - ++ plug
但是不清楚我应该如何调用它,特别是(特别是)实现读取
和任意的
(对于QuickCheck)。
对于前者,我可以得到尽可能多的
实例读取EnigmaConfig其中
readPrec _ i = case runExcept(configEnigma c w s r)of
Right cfg - > [(cfg,)]
左错误 - >未定义
其中[c,w,s,r] =单词i
但这似乎最终在 err
中隐藏错误信息;而对于后者,我被困在
实例任意EnigmaConfig其中
whatever = do
nc < - 选择(3,4) - 这可以覆盖更广泛的范围
ws< - replicateM nc capitals
cs< - replicateM nc(elements rotors)
uk<元素反射器
rs< - replicateM nc(choose(1,26))
return $ configEnigma(插入 - (uk:cs))
ws
UX。 MO.KZ.AY.EF.PL - TBD - 生成插件板并测试<<
(intercalate。$(printf%02d)< $>(rs :: [Int]))
如果预期和实际类型不匹配,则失败:
预期类型:Gen EnigmaConfig
实际类型:Gen(transformers-0.4.2.0:Control.Monad.Trans.Except.Except Crypto.Enigma.EnigmaError EnigmaConfig)
如果在可能失败时,如何调用(public safe)构造函数,特别是在实现时读取
和任意
为我的类?
读取
类型类代表分析成功的列表(失败与没有成功相同);所以不要 undefined
你应该返回 []
。至于丢失有关出现错误的信息:这是真的,并且 readPrec
的类型意味着您不能做太多事情。如果你真的,真的想[注意:我不认为你应该这样]你可以定义一个新的包装器围绕除了EnigmaError EnigmaConfig
,并给出一个读取成功解析配置错误的
实例。
对于任意
你有几个选择。一个选择是所谓的拒收抽样;例如
whatever = do
- ...
case configEnigma ... of
左错误 - >任意 - 再试一次
右v - >返回v
您还可以考虑使用任意
实例成为内部API的一部分,并使用不安全的内部调用,而不是使用安全的公共API来构建您的配置。其他选项包括调用错误
或 fail
。 (我认为这四个选项大致偏好排序 - 拒绝抽样,然后不安全的内部调用,然后错误
,然后 fail
- 虽然你的判断可能有所不同。)
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
but it is unclear how I should call this, particularly (and specifically) in implementing Read
and Arbitrary
(for QuickCheck).
For the former, I can get as far as
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
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:
Expected type: Gen EnigmaConfig Actual type: Gen (transformers-0.4.2.0:Control.Monad.Trans.Except.Except Crypto.Enigma.EnigmaError EnigmaConfig)
How do I call a ("public safe") constructor when it may fail, particularly when using it in implementing Read
and Arbitrary
for my class?
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
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.)
这篇关于如何调用可能失败的构造函数,特别是在实现“读取”和“任意”时?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!