我如何验证Haskell的参数“public safe”构造函数? [英] How do I validate arguments to a Haskell "public safe" constructor?

查看:143
本文介绍了我如何验证Haskell的参数“public safe”构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的 Python包中,我有一个函数用于创建经过验证的实例类,类似于

  @staticmethod 
def config_enigma(rotor_names,window_letters,plugs,rings):
'b $ b comps =(rotor_names +' - '+ plugs).split(' - ')[:: - 1]
winds = [num_A0(c)for'A'+ window_letters +' A'] [:: - 1]
rngs = [int(x)for x in('01。'+ rings +'.01')。split('。')] [:: - 1]

声明所有(名字在旋转中的名称在comps [1:-1])$ ​​b $ b在反射器中声明comps [-1]
声明len(rngs)== len风)== len(comps)
断言所有(rng中1 <= rng <= 26)
断言所有风(风中的chr_A0(风))风

#...

我想在Haskell中执行相同的行为。但是以相同的方式这样做(带断言)不起作用,因为一般情况下Haskell断言是被禁用的(除非设置了某些编译器标志)。例如,在像< /($和$($($和$($($($)$))
$ b $ pref =lang-hs prettyprint-override> configEnigma rots winds plug rngs = ==(长度分量'))< $> [长度风',长度rng'])&
(和$ [(> = 1),(<= 26)]< (和$(`elem`字母)< $>')&&
(和$(`M.member` comps )< $>尾部组件'))
- ...
其中
rngs'=反向$(读< $>(splitOn。$01。 ++ rngs ++.01):: [Int])
winds'=A++反向风++A
组件'=反向$ splitOn - $不能依赖于工作,因为断言将会在大多数情况下被删除。



什么是惯用的a nd可靠的方式来强制所有的实例在Haskell中验证(使用公共安全构造函数)?

解决方案

正常事情是明确表达失败。例如,可以写成:

  configEnigma :: ...  - >也许... 
configEnigma ... = do
guard(全部(((==)`on长度)组件')[winds',rngs'])
guard(all (inRange(1,26))rngs')
guard(all(`elem` letters)winds')
guard(all(`M.member` comps)(tail components'))
return ...
其中
...

您甚至可能考虑对某些自定义类型错误 Maybe 升级到 Except Error $ c>,以告诉来电者在施工过程中出了什么问题。然后,您可以使用像 $ b

  guard  除非(all(inRange(1,26))rngs')(throwError OutOfRange)

< configEnigma 的调用者必须表达如何处理失败。对于可能,这看起来像
$ b

 案例configEnigma ... of 
Just v - > - 使用配置的enigma机器v
Nothing - > - 失败案例;可能会打印一条消息给用户,并退出

,同时使用你得到了有关错误的信息:

case runExcept(configEnigma ... )
Right v - > - 使用配置的enigma机器v
Left err - > - 失败案例;根据错误告知用户究竟出了什么问题,然后退出


In my Python package I have a function I use to create validated instances of my class, with something like

@staticmethod
def config_enigma(rotor_names, window_letters, plugs, rings):

    comps = (rotor_names + '-' + plugs).split('-')[::-1]
    winds = [num_A0(c) for c in 'A' + window_letters + 'A'][::-1]
    rngs = [int(x) for x in ('01.' + rings + '.01').split('.')][::-1]

    assert all(name in rotors for name in comps[1:-1])
    assert comps[-1] in reflectors
    assert len(rngs) == len(winds) == len(comps)
    assert all(1 <= rng <= 26 for rng in rngs)
    assert all(chr_A0(wind) in LETTERS for wind in winds)

    #...

and I would like to enforce the same behavior in Haskell. But doing so in the same way — with assertions — does not work, because Haskell assertions are disabled in general (unless certain compiler flags are set). For example, in something like

configEnigma rots winds plug rngs =
    assert ((and $ (==(length components')) <$> [length winds', length rngs']) &&
            (and $ [(>=1),(<=26)] <*> rngs') &&
            (and $ (`elem` letters) <$> winds') &&
            (and $ (`M.member` comps) <$> tail components'))
    -- ...
    where
        rngs' = reverse $ (read <$> (splitOn "." $ "01." ++ rngs ++ ".01") :: [Int])
        winds' = "A" ++ reverse winds ++ "A"
        components' = reverse $ splitOn "-" $ rots ++ "-" ++ plug

can't be relied on to work because the assertions will be removed in most contexts.

What is an idiomatic and reliable way to force all my instances to be validated in Haskell (using a "public safe" constructor)?

解决方案

The normal thing is to express failure explicitly. For example, one might write

configEnigma :: ... -> Maybe ...
configEnigma ... = do
    guard (all (((==) `on` length) components') [winds', rngs'])
    guard (all (inRange (1,26)) rngs')
    guard (all (`elem` letters) winds')
    guard (all (`M.member` comps) (tail components'))
    return ...
    where
    ...

You might even consider upgrading from Maybe to Except Error for some custom-defined type Error, to communicate to the caller what it was that went wrong during construction. Then instead of guard, you could use a construction like:

    unless (all (inRange (1,26)) rngs') (throwError OutOfRange)

The caller to configEnigma will have to express how to handle failures. For Maybe, this looks like

case configEnigma ... of
    Just v  -> -- use the configured enigma machine v
    Nothing -> -- failure case; perhaps print a message to the user and quit

while with Except you get information about what went wrong:

case runExcept (configEnigma ...) of
    Right v  -> -- use the configured enigma machine v
    Left err -> -- failure case; tell the user exactly what went wrong based on err and then quit

这篇关于我如何验证Haskell的参数“public safe”构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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