我经常有这样的功能:
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以限制其范围?
答案 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)
forall
将a
的范围扩展到其原始类型签名之外,以涵盖实现中出现的任何类型签名。这意味着在loop :: a -> [a]
中,a
与a
签名中的foo
绑定。