Haskell 为什么有";映射(过滤器fst)“;“类型”;[Bool,a]->;[(Bool,a)]”;?
我试图理解为什么这个函数Haskell 为什么有";映射(过滤器fst)“;“类型”;[Bool,a]->;[(Bool,a)]”;?,haskell,types,functional-programming,type-inference,unification,Haskell,Types,Functional Programming,Type Inference,Unification,我试图理解为什么这个函数 map (filter fst) 有那种 [[(Bool, a)]] -> [[(Bool, a)]] 如果filter必须接收一个返回Bool类型的函数,而fst只返回元组的第一个元素,“filter-fst”如何工作 filter :: (a -> Bool) -> [a] -> [a] fst :: (a, b) -> a 谁能给我解释一下吗?谢谢;) fst是一个返回布尔值的函数,只要您限制元组的第一个元素为布尔值(第二个元素
map (filter fst)
有那种
[[(Bool, a)]] -> [[(Bool, a)]]
如果filter必须接收一个返回Bool类型的函数,而fst只返回元组的第一个元素,“filter-fst”如何工作
filter :: (a -> Bool) -> [a] -> [a]
fst :: (a, b) -> a
谁能给我解释一下吗?谢谢;)
fst
是一个返回布尔值的函数,只要您限制元组的第一个元素为布尔值(第二个元素可以是任何元素,因此(Bool,a)
但是,我们也可以将类型变量交换为fst
,以获得:
:t fst -- (c,b) -> c
因此,filter
的第一个参数的类型是a->Bool
,而fst
本身是(c,b)->c
。现在我们尝试将其组合起来(我认为这称为统一):
由此,我们可以推断c
必须是Bool
(因为右手侧必须相等)并获得:
1st arg of filter: a -> Bool
fst: (Bool,b) -> Bool
1st arg of filter: (Bool,b) -> Bool
fst: (Bool,b) -> Bool
从上面,我们推断a
必须是(Bool,b)
,并获得:
1st arg of filter: a -> Bool
fst: (Bool,b) -> Bool
1st arg of filter: (Bool,b) -> Bool
fst: (Bool,b) -> Bool
我们完成了
如果filter必须接收一个返回Bool类型的函数,而fst只返回元组的第一个元素,“filter-fst”如何工作
从某种意义上说,你已经回答了自己的问题!让我们把它分解一下:
筛选器必须接收返回布尔类型的函数
好的,让我们看看你传递的是什么:fst
。fst
是一个函数吗?是的,它是,所以我们已经记下了第一部分。它是否返回一个Bool
?好吧,让我们看看它的功能:
fst只返回元组的第一个元素
因此,如果元组的第一个元素是Bool
,那么是的,它确实返回Bool!但是,如果元组的第一个元素不是Bool
,它不会并且将无法通过类型检查
让我们再看一看您设置的类型。我将更改类型变量的名称,以使事情更清楚:
filter :: (a -> Bool) -> [a] -> [a]
fst :: (b, c) -> b
fst
接受一个(b,c)
并返回一个b
,而filter期望一个函数接受a
并返回一个Bool
。我们正在传入fst
,因此上面的a
必须是(b,c)
因为这是fst
的第一个参数。我们传递到filter
的函数的返回值必须是aBool
,因此上面的b
必须是aBool
。而c
可以是任何东西,因为它根本不被filter使用。用这些值替换a
和b
为我们提供了过滤器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)]
,换句话说,d
和e
都是(Bool,c)
。将它们插入函数,我们得到最终类型:
map (filter fst) :: [[(Bool, c)]] -> [[(Bool, c)]]
因为在danielpwright的答案发布之前我已经写下了这篇文章,所以我还是发布了它。我只是在这里回顾一下我对
过滤器fst
类型的思考过程
首先,写下类型签名(更改fst,使其类型变量名不会与筛选器的类型变量名冲突):
将(a->Bool)
与((b,c)->b)
匹配:
b
必须是Bool
,这意味着a
必须是(Bool,c)
通过使用此信息专门化过滤器
,它将成为:
filter :: ((Bool,c) -> Bool) -> [(Bool,c)] -> [(Bool,c)]
这导致了
filter fst :: [(Bool, c)] -> [(Bool, c)]
你真的只需要解一些类型方程。让我们开始:
filter :: (a -> Bool) -> [a] -> [a]
fst :: (b, c) -> b
因此,filter fst::[a]->[a]
其中a
是以下类型方程的解:
a -> Bool = (b, c) -> b
a -> b = [(Bool, c)] -> [(Bool, c)]
这意味着
a = (b, c)
Bool = b
a = (Bool, c)
a = [(Bool, c)]
b = [(Bool, c)]
这意味着
a = (b, c)
Bool = b
a = (Bool, c)
a = [(Bool, c)]
b = [(Bool, c)]
因此,过滤器fst::[(Bool,c)]->[(Bool,c)]
现在我们有:
map :: (a -> b) -> [a] -> [b]
filter fst :: [(Bool, c)] -> [(Bool, c)]
因此,map(filter fst)::[a]->[b]
其中a
和b
由以下类型方程给出:
a -> Bool = (b, c) -> b
a -> b = [(Bool, c)] -> [(Bool, c)]
这意味着
a = (b, c)
Bool = b
a = (Bool, c)
a = [(Bool, c)]
b = [(Bool, c)]
因此,
map(filter fst)::[[(Bool,c)]->[[(Bool,c)]]
正如其他人所做的那样,我想在这里解类型方程;但我想以更直观的方式写下它们,这样可以以更自动的方式进行推导。让我们看看
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程序,请参见。我认为您要查找的术语是“限制”(即限制类型变量的类型)。术语“统一”用于逻辑编程。它是一种执行策略,通过统一逻辑谓词中的变量使逻辑谓词为真。简单地说,“统一”是一个反复试验的过程。它用于确定值的有效性。与“限制”相反这不是一个反复试验的过程。它就像一次完成任务的过程。虽然这两个概念相似,但它们肯定不一样。@AaditMShah:不,这是unifica