2014-02-08 43 views
1

这是我定义的函数任何”当我尝试在Haskell中编写本地函数的类型定义时,为什么会出现此错误?

any' :: (t -> Bool) -> [t] -> Bool 
any' f = foldl' step False 
     where step :: Bool -> t -> Bool 
      step b x | f x  = True 
         | otherwise = b 

我拥抱时加载此错误:

ERROR "folds.hs":65 - Inferred type is not general enough 
*** Expression : step 
*** Expected type : Bool -> a -> Bool 
*** Inferred type : Bool -> _7 -> Bool 

...这ghci的中:

folds.hs:65:27: 
    Couldn't match expected type `t' with actual type `t1' 
     `t' is a rigid type variable bound by 
      the type signature for any' :: (t -> Bool) -> [t] -> Bool 
      at folds.hs:62:9 
     `t1' is a rigid type variable bound by 
      the type signature for step :: Bool -> t1 -> Bool at folds.hs:64:22 
    In the first argument of `f', namely `x' 
    In the expression: f x 
    In a stmt of a pattern guard for 
        an equation for `step': 
     f x 

当我删除步骤的类型定义它工作正常,所以我的问题是... 有一种方法来正确编写该类型的定义,或者我正在处理其中一个无法明确输入本地函数的地方?

+1

您需要的作用域类型变量扩展,否则编译器看到第二个T作为另一个新的多态类型的变量,而不是相同的T作为主要的类型声明。 –

+1

顺便说一句,为什么foldl'而不foldr? –

+0

你是对的Sassa NF,foldr是更好的,因为允许甚至“任何”的东西(重复2)“ – matiascelasco

回答

3

在签名

where step :: Bool -> t -> Bool 

t是不一样的t作为在any'签名发生。相反,它被解释为step签名的局部新鲜类型变量。

换句话说,你的代码实际上相当于

any' :: (t -> Bool) -> [t] -> Bool 
any' f = foldl' step False 
    where step :: Bool -> a -> Bool   -- t renamed to a 
     step b x | f x  = True 
        | otherwise = b 

,然后编译器会抱怨,因为在其签名step索赔,同时接口,需要一个t是适用于任何类型的a

可能的修复方法是删除step签名。在这种情况下,编译器会自行推断正确的类型。从程序员的角度来看,这并不令人愉快,因为编译器现在不会检查程序员打算使用的签名。

正如评论中所建议的,另一个修正是启用一个Haskell扩展,它允许我们编写我们的类型。

{-# LANGUAGE ScopedTypeVariables #-} 
import Data.List 

any' :: forall t. (t -> Bool) -> [t] -> Bool 
any' f = foldl' step False 
     where step :: Bool -> t -> Bool 
      step b x | f x  = True 
         | otherwise = b 

这里明确量词forall t告诉发生的any'定义里面的t编译器是真的一样t,而不是一个新的类型变量。

+0

谢谢!很好的答案。 您是否知道使用该扩展名有什么缺点?我猜是有的,否则会默认启用。 – matiascelasco

+1

@matiascelasco没有缺点,但因为编译器必须支持它。 GHC确实。 – chi

+0

这里明确的'forall'真的有必要吗?我认为这不是...... – MathematicalOrchid

1

您需要的作用域类型变量扩展,否则编译器看到第二t成一个新的多态型的变量,而不是同一t在主类型声明。

发生这种情况的线索在消息Couldn't match expected type 't' with actual type 't1'。显然ghci已经重新命名为第二个tt1,因为它不认为它们是相同的。

您可以使用ScopedTypeVariables和明确的forallt纳入内部函数的作用域。

{-# LANGUAGE ScopedTypeVariables #-} 

any' :: forall t.(t -> Bool) -> [t] -> Bool 
any' f = foldl' step False 
     where step :: Bool -> t -> Bool 
      step b x | f x  = True 
         | otherwise = b 
相关问题