如何调用可能失败的构造函数,特别是在实现“读取”和“任意”时? [英] How do I call a constructor that may fail, especially when implementing 'Read' and 'Arbitrary'?

查看:136
本文介绍了如何调用可能失败的构造函数,特别是在实现“读取”和“任意”时?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

 数据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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆