模式隐藏功能

时间:2010-12-10 06:45:17

标签: haskell

我经常有这样的功能:

fooInner :: Int -> Int -> [Int] -> [Int]
fooInner 0 base accum = accum
fooInner num base accum = fooInner c base ([r] ++ accum)
                   where ....

foo :: Int -> Int -> [Int]
foo a b = numNotationInner a b []

我创造了'foo'来更舒适地使用功能。而不是fooInner 10 4 []我可以使用foo 10 4。

有没有办法在foo中'隐藏'fooInner以限制其范围?

1 个答案:

答案 0 :(得分:9)

在这种情况下,您可以隐藏where块中的内容:

foo :: Int -> Int -> [Int]
foo a b = fooInner a b [] where
    fooInner :: Int -> Int -> [Int] -> [Int]
    fooInner 0 base accum = accum
    fooInner num base accum = fooInner c base ([r] ++ accum)
        where ...

虽然您可能会遇到几个问题:

  • 如果实现有多种模式,where块只适用于其中一种模式:

    foo :: Int -> Int -> [Int]
    foo 0 b = fooInner 1 b [] -- fooInner not in scope
    foo a b = fooInner a b [] where
        ... define fooInner ...
    

    除非Haskell有一些我不知道的语法特性,否则如果你还想模式匹配,你将不得不做这样的事情:

    foo a b = case (a, b) of
                   (0, b) -> fooInner 1 b []
                   (a, b) -> fooInner a b []
        where
            ... define fooInner ...
    

    我没有测试过这个。您可能需要修补空白以消除语法错误。

  • 如果您的函数具有多态类型,则可能会在尝试向内部函数添加类型签名时遇到麻烦:

    foo :: (a -> a) -> a -> [a]
    foo f z = loop z where
        -- loop :: a -> [a] -- causes a type error
        loop z = z : loop (f z)
    

    类型签名loop :: a -> [a]的问题在于,在其上下文中,它并不真正适用于所有 a,只有a对应传递给foo的参数。因为它使用f,所以类型绑定到a,这意味着它不再适用于任何a

    这里的简单解决方案是不使用类型签名。但是,如果您确实需要甚至需要类型签名,但添加一个会导致类型错误,请启用ScopedTypeVariables扩展名,然后执行以下操作:

    foo :: forall a. (a -> a) -> a -> [a]
    foo f z = loop z where
        loop :: a -> [a]
        loop z = z : loop (f z)
    

    foralla的范围扩展到其原始类型签名之外,以涵盖实现中出现的任何类型签名。这意味着在loop :: a -> [a]中,aa签名中的foo绑定。