为什么“map(filter fst)”类型为“[[(Bool,a)]] - > [[(Bool,a)]]”?

时间:2014-03-19 11:45:22

标签: haskell types functional-programming type-inference unification

我试图理解为什么功能

map (filter fst)

的类型为

[[(Bool, a)]] -> [[(Bool, a)]]

如何"过滤fst"如果filter必须接收一个返回Bool-Type的函数,fst只返回一个元组的第一个元素?

filter :: (a -> Bool) -> [a] -> [a]
fst :: (a, b) -> a

有人能解释我吗?谢谢;)

6 个答案:

答案 0 :(得分:16)

  

如何"过滤fst"如果过滤器必须接收一个返回Bool-Type的函数,fst只返回一个元组的第一个元素?

从某种意义上说,你已经回答了自己的问题!让我们分解一下:

  

过滤器必须接收一个返回Bool-Type

的函数

好的,让我们来看看你传递的内容:fstfst是一个函数吗?是的,是的,所以我们已经把第一部分打倒了。它会返回Bool吗?那么,让我们来看看它的作用:

  

fst只返回元组的第一个元素

因此,如果元组的第一个元素是Bool,那么是的,它确实会返回一个bool!但是,如果元组的第一个元素不是Bool,那么它就没有了,并且将无法进行类型检查。

让我们再看看你提出的类型。我要更改类型变量的名称只是为了让事情更清楚:

filter :: (a -> Bool) -> [a] -> [a]
fst :: (b, c) -> b

fst需要(b, c)并返回b,过滤器需要一个a并返回Bool的函数。我们正在传递fst,因此上面的a必须是(b, c),因为它是fst的第一个参数。我们传递到filter的函数的返回值必须是Bool,因此上面的b必须是Boolc可以是任何内容,因为过滤器根本不使用它。替换ab的值为我们提供了filter fst的最终类型:

filter fst :: [(Bool, c)] -> [(Bool, c)]

最后,map的类型是:

map :: (d -> e) -> [d] -> [e]

(同样,我在这里重新命名了类型变量,只是为了将它们与我们上面使用过的那些区分开来,但请记住它实际上并不重要,因为它们所谓的是什么只要它们在类型注释的范围内保持一致)

map (filter fst)将我们在上面定义的filter fst作为第一个参数传递给map。将d的参数替换为e的结果,我们可以看到此函数必须为[(Bool, c)] -> [(Bool, c)],换句话说,de都是{{1} }}。将这些插入到我们到达最终类型的函数中:

(Bool, c)

答案 1 :(得分:2)

fst是一个返回布尔值的函数,只要你限制元组将布尔值作为它们的第一个元素(第二个元素可以是任何东西,因此(Bool, a)

答案 2 :(得分:1)

:t filter    -- (a -> Bool) -> [a] -> [a]
:t fst       -- (a,b) -> a

但是,我们也可以交换fst的类型变量来获取:

:t fst       -- (c,b) -> c

因此,filter的第一个参数类型为a -> Bool,而fst本身为(c,b) -> c。现在我们尝试将它结合起来(我认为这称为统一):

1st arg of filter:  a     -> Bool
fst:                (c,b) -> c

由此我们可以推断c必须是Bool(因为右边必须相等)并获得:

1st arg of filter:  a        -> Bool
fst:                (Bool,b) -> Bool

从上面我们推断,a必须是(Bool,b),并获得:

1st arg of filter:  (Bool,b) -> Bool
fst:                (Bool,b) -> Bool

我们已经完成了。

答案 3 :(得分:1)

由于我已经在danielpwright的回答发布之前写下了这篇文章,所以无论如何我发布了它。我现在正在通过我的思维过程查看filter fst的类型。

首先,记下类型签名(更改fst,使其类型变量名称不与过滤器的名称冲突):

filter :: (a -> Bool) -> [a] -> [a]
fst :: (b, c) -> b

(a -> Bool)((b, c) -> b)匹配:

b必须为Bool,这意味着a必须为(Bool,c)

通过使用此信息对filter进行专门化,它变为:

filter :: ((Bool,c) -> Bool) -> [(Bool,c)] -> [(Bool,c)]

导致

filter fst :: [(Bool, c)] -> [(Bool, c)]

答案 4 :(得分:1)

你真的只需要解决一些类型方程式。让我们开始吧:

 filter :: (a -> Bool) -> [a] -> [a]
 fst    :: (b, c) -> b

因此,filter fst :: [a] -> [a]其中a是以下类型等式的解决方案:

 a -> Bool = (b, c) -> b

暗示

 a    = (b, c)
 Bool = b

暗示

 a = (Bool, c)

因此,filter fst :: [(Bool, c)] -> [(Bool, c)]

现在我们有:

map        :: (a -> b) -> [a] -> [b]
filter fst :: [(Bool, c)] -> [(Bool, c)]

所以,map (filter fst) :: [a] -> [b]其中ab由以下类型公式给出:

a -> b = [(Bool, c)] -> [(Bool, c)]

暗示

a = [(Bool, c)]
b = [(Bool, c)]

因此,map (filter fst) :: [[(Bool, c)]] -> [[(Bool, c)]]

答案 5 :(得分:1)

正如其他人所做的那样,我想在这里解决类型方程;但是我想以更直观的方式将它们写下来,因此可以以更自动的方式执行派生。我们来看看。

filter :: (  a   -> Bool) -> [a] -> [a]
fst    ::  (c,d) -> c              -- always use unique type var names, 
------------------------           --   rename freely but consistently
filter fst :: [a] -> [a],    a -> Bool  ~  (c,d) -> c
                             a ~ (c,d)  ,  Bool ~ c
                             a ~ (Bool,d)
           ---------------------------
           :: [(Bool,d)] -> [(Bool,d)]

纯机械的东西。 :)有了这个,

map        :: (     a     ->     b     ) -> [a] -> [b]
filter fst ::  [(Bool,d)] -> [(Bool,d)]
------------------------------
map (filter fst) :: [a] -> [b],     a ~ [(Bool,d)]  ,  b ~ [(Bool,d)]
                 -------------------------------
                 :: [[(Bool,d)]] -> [[(Bool,d)]]

最终类型中的类型变量可以自由重命名以便于阅读(当然,以一致的方式)。

我的答案唯一能补充到其他答案中已经显示的内容,就是这个建议(我认为这很重要):如果你遵循这个简单的写下一个低于另一个的原则,以非常机械,自动的方式执行这些类型的统一变得非常容易(从而减少出错的可能性)。

另一个例子,包括实际的类型派生Prolog程序,请参阅Haskell: how to infer the type of an expression manually