如何调用可能会失败的构造函数,尤其是在实现 'Read' 和 'Arbitrary' 时?

How do I call a constructor that may fail, especially when implementing 'Read' and 'Arbitrary'?

我有一个 "public safe" 可能会因(可能提供信息的)错误而失败:

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

但不清楚我应该如何称呼它,特别是(特别是)在实施 ReadArbitrary(对于 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 中可用的错误信息;而对于后者,我停留在

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]))

因预期类型与实际类型不匹配而失败:

Expected type: Gen EnigmaConfig Actual type: Gen (transformers-0.4.2.0:Control.Monad.Trans.Except.Except Crypto.Enigma.EnigmaError EnigmaConfig)

当 ("public safe") 构造函数可能失败时如何调用它,特别是在为我的 class 实现 ReadArbitrary 时使用它时?

Read 类型类将解析表示为成功列表(失败与不成功相同);所以你应该 return [] 而不是 undefined。至于丢失有关出错信息的信息:是的,readsPrec 的类型意味着您对此无能为力。如果你真的,真的想要[注意:我认为你不应该想要这个]你可以定义一个围绕 Except EnigmaError EnigmaConfig 的新类型包装器并给它一个成功解析配置错误的 Read 实例。

Arbitrary 你有几个选择。一种选择是所谓的拒绝抽样;例如

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

您也可以考虑将 Arbitrary 实例作为内部 API 的一部分,并使用不安全的内部调用而不是使用安全的 public API用于构建您的配置。其他选项包括调用 errorfail。 (我认为这四个选项的优先顺序大致是——拒绝抽样,然后是不安全的内部调用,然后是 error,然后是 fail——尽管你的判断可能会有所不同。)