如何调用可能会失败的构造函数,尤其是在实现 '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
但不清楚我应该如何称呼它,特别是(特别是)在实施 Read
和 Arbitrary
(对于 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 实现 Read
和 Arbitrary
时使用它时?
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用于构建您的配置。其他选项包括调用 error
或 fail
。 (我认为这四个选项的优先顺序大致是——拒绝抽样,然后是不安全的内部调用,然后是 error
,然后是 fail
——尽管你的判断可能会有所不同。)
我有一个 "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
但不清楚我应该如何称呼它,特别是(特别是)在实施 Read
和 Arbitrary
(对于 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 实现 Read
和 Arbitrary
时使用它时?
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用于构建您的配置。其他选项包括调用 error
或 fail
。 (我认为这四个选项的优先顺序大致是——拒绝抽样,然后是不安全的内部调用,然后是 error
,然后是 fail
——尽管你的判断可能会有所不同。)