Haskell 如何调用可能失败的构造函数,特别是在实现“Read”和“arbitral”时?

Haskell 如何调用可能失败的构造函数,特别是在实现“Read”和“arbitral”时?,haskell,error-handling,exception-handling,quickcheck,Haskell,Error Handling,Exception Handling,Quickcheck,我有一个公共保险箱,可能会出现潜在的信息错误: data EnigmaError = BadRotors | BadWindows | MiscError String instance Show EnigmaError where show BadRotors = "Bad rotors" show BadWindows = "Bad windows" show (MiscError str) = str co

我有一个公共保险箱,可能会出现潜在的信息错误:

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
但这似乎最终隐藏了err中可用的错误信息;而对于后者,我则陷入了困境

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]))
如果预期类型与实际类型不匹配,则失败:

预期类型:Gen EnigmaConfig 实际类型:发电机变压器-0.4.2.0:Control.Monad.Trans.Exception.Exception.Crypto.Enigma.EnigmaError EnigmaConfig

当公共安全构造函数可能失败时,特别是在为我的类实现Read和arbitral时,如何调用它?

Read typeclass将解析表示为成功列表,失败与否相同;因此,您应该返回[],而不是未定义。至于丢失错误信息:这是真的,readsPrec的类型意味着你对此无能为力。如果你真的想[注意:我认为你不应该想要这个],你可以在EnigmaError EnigmaConfig之外定义一个新的类型包装器,并给它一个读取实例,该实例成功解析了配置错误

对于武断,你有两个选择。一种选择是所谓的拒绝抽样;e、 g

arbitrary = do
    -- ...
    case configEnigma ... of
        Left err -> arbitrary -- try again
        Right v  -> return v

您也可以将任意实例视为内部API的一部分,使用不安全的内部调用,而不是使用安全、公共API来构造配置。其他选项包括调用错误或失败。我认为这四个选项大致上是优先顺序-拒绝抽样,然后是不安全的内部调用,然后是错误,然后失败——尽管您的判断可能有所不同。

因为Read应该完全失败,并且因为要通知,而不是未定义或[],错误显示错误是什么?同样在武断的情况下,因为我只为参数生成有效值,我希望它立即失败,而不是悄悄地尝试另一个;所以error show err而不是任意的?@raxacoricofallapatorius如果你想写一个包含信息错误的解析器,你可以自由地;你就是不能,不应该叫它读。啊,我明白了。我在docs for Read中遗漏了这一点:无论是什么原因,Read都应该失败,没有解析,readsPrec应该返回[],正如您所说的:。这对用户imv来说太糟糕了,因为信息丢失了;但这些就是我猜想的规则。不过,对于任意EnigmaConfig,如果我生成的参数都是有效的,我当然不想做这么酷的拒绝采样:我只是想失败,我想:这将是一个真正的编码错误。请注意,readsPrec中的信息丢失,以及其他可能的情况,如果构造器出现错误而失败,则可以避免此问题–请参阅我最近对的评论。
arbitrary = do
    -- ...
    case configEnigma ... of
        Left err -> arbitrary -- try again
        Right v  -> return v