为什么不需要在此函数中提供参数?
Why isn't it necessary to provide a parameter in this function?
我是 Haskell 的新手,本周我在几个讲座幻灯片中发现了这个特殊功能。我试图理解为什么以下函数不需要包含参数:
-- Return all final segments of the argument, longest first
-- (horrible runtime complexity, used here for illustration purposes only)
tails :: [a] -> [[a]]
tails = reverse . map reverse . inits . reverse
如果我像 tails "thisisastring"
那样称呼它,那么这将是一个有效的论点。是不是必须提供一个参数,比如tails xs = ...
。我以前看到的所有其他功能都是这种方式。
该参数是隐式的。或者换句话说,reverse . map reverse . inits . reverse
求值为 [a] -> [[a]]
.
类型的函数
考虑一个更简单的例子:
double_impl x = x * 2
double = double_impl
这里的double
的类型和double_impl
的类型是一样的,即它接受一个typeclass Num
:
的参数
main = do
print $ double_impl 5
print $ double 5
-- Out: 10
-- Out: 10
我们可以看到 tails
是一个函数,通过检查它的类型。
为了计算它的类型,我们首先记下组合中所有中间函数的类型。请注意,我们为函数的每次出现使用新类型变量。
reverse :: [a] -> [a]
inits :: [b] -> [[b]]
map :: (c -> d) -> [c] -> [d]
现在我们 map reverse
的类型为 [[e]] -> [[e]]
因为我们通过比较表达式
得到了某种类型 e
的 c=d=[e]
reverse :: c -> d -- for some c and d
reverse :: [e] -> [e] -- for some e
因此最后两个中间体有类型
map reverse :: [[e]] -> [[e]]
reverse :: [f] -> [f]
现在我们开始尝试匹配类型。首先让我强调一下,显然这些不是真实的类型! (抱歉全部大写,但我不想让任何人错过。)
inits . reverse :: [a] -*- [a] = [b] -*> [[b]]
-- I'm using a -*- b -*> c to denote the type a -> c obtained by
-- composing a function of type a -> b with one of type b -> c.
-- The *s are to break the double dashes up,
-- so they aren't parsed as a comment.
-- Anyway, looking at this type, we see
-- we must have [a] = [b], so a = b
-- we can rewrite the type of inits . reverse as
inits . reverse :: [a] -> [[a]]
那么接下来的作文:
map reverse . inits . reverse :: [a] -*- [[a]] = [[e]] -*> [[e]]
-- again, we have [[a]] = [[e]], so e = a, and we have
map reverse . inits . reverse :: [a] -> [[a]]
最后,我们有
reverse . map reverse . inits . reverse :: [a] -*- [[a]] = [f] -*> [f]
-- This time we have [[a]] = [f], so we must have f = [a], so the type
-- of the final composition is
tails = reverse . map reverse . inits . reverse :: [a] -> [[a]]
由于 tails
的类型为 [a] -> [[a]]
,它必须是一个接受 a
列表作为参数的函数,并且 returns 接受列表列表的函数a
s.
这称为无点样式(其中 "point" 是一个数学术语,在这里基本上意味着 "argument")。
即使 tails xs = ...
只是 tails = \xs -> ...
的语法糖,所以你需要做的就是让自己相信 tails
是一个函数,就是认识到
reverse
、map reverse
、inits
都是函数:
map
是高阶函数;它接受一个函数作为参数,returns 另一个函数。
map reverse
是一个函数,因为 map
应用于函数 reverse
。
- 两个函数的组合是另一个函数(假设类型匹配,这样我们就可以专注于每个组合的结果,而不是验证每个组合类型检查。)
因此
reverse . map reverse
是一个函数,
- 所以
reverse . map reverse . inits
是一个函数,
- 和
reverse . map reverse . inits . reverse
是一个函数。
由于tails
被赋予了reverse . map reverse . inits . reverse
的值,tails
本身也是一个函数
我是 Haskell 的新手,本周我在几个讲座幻灯片中发现了这个特殊功能。我试图理解为什么以下函数不需要包含参数:
-- Return all final segments of the argument, longest first
-- (horrible runtime complexity, used here for illustration purposes only)
tails :: [a] -> [[a]]
tails = reverse . map reverse . inits . reverse
如果我像 tails "thisisastring"
那样称呼它,那么这将是一个有效的论点。是不是必须提供一个参数,比如tails xs = ...
。我以前看到的所有其他功能都是这种方式。
该参数是隐式的。或者换句话说,reverse . map reverse . inits . reverse
求值为 [a] -> [[a]]
.
考虑一个更简单的例子:
double_impl x = x * 2
double = double_impl
这里的double
的类型和double_impl
的类型是一样的,即它接受一个typeclass Num
:
main = do
print $ double_impl 5
print $ double 5
-- Out: 10
-- Out: 10
我们可以看到 tails
是一个函数,通过检查它的类型。
为了计算它的类型,我们首先记下组合中所有中间函数的类型。请注意,我们为函数的每次出现使用新类型变量。
reverse :: [a] -> [a]
inits :: [b] -> [[b]]
map :: (c -> d) -> [c] -> [d]
现在我们 map reverse
的类型为 [[e]] -> [[e]]
因为我们通过比较表达式
e
的 c=d=[e]
reverse :: c -> d -- for some c and d
reverse :: [e] -> [e] -- for some e
因此最后两个中间体有类型
map reverse :: [[e]] -> [[e]]
reverse :: [f] -> [f]
现在我们开始尝试匹配类型。首先让我强调一下,显然这些不是真实的类型! (抱歉全部大写,但我不想让任何人错过。)
inits . reverse :: [a] -*- [a] = [b] -*> [[b]]
-- I'm using a -*- b -*> c to denote the type a -> c obtained by
-- composing a function of type a -> b with one of type b -> c.
-- The *s are to break the double dashes up,
-- so they aren't parsed as a comment.
-- Anyway, looking at this type, we see
-- we must have [a] = [b], so a = b
-- we can rewrite the type of inits . reverse as
inits . reverse :: [a] -> [[a]]
那么接下来的作文:
map reverse . inits . reverse :: [a] -*- [[a]] = [[e]] -*> [[e]]
-- again, we have [[a]] = [[e]], so e = a, and we have
map reverse . inits . reverse :: [a] -> [[a]]
最后,我们有
reverse . map reverse . inits . reverse :: [a] -*- [[a]] = [f] -*> [f]
-- This time we have [[a]] = [f], so we must have f = [a], so the type
-- of the final composition is
tails = reverse . map reverse . inits . reverse :: [a] -> [[a]]
由于 tails
的类型为 [a] -> [[a]]
,它必须是一个接受 a
列表作为参数的函数,并且 returns 接受列表列表的函数a
s.
这称为无点样式(其中 "point" 是一个数学术语,在这里基本上意味着 "argument")。
即使 tails xs = ...
只是 tails = \xs -> ...
的语法糖,所以你需要做的就是让自己相信 tails
是一个函数,就是认识到
reverse
、map reverse
、inits
都是函数:map
是高阶函数;它接受一个函数作为参数,returns 另一个函数。map reverse
是一个函数,因为map
应用于函数reverse
。
- 两个函数的组合是另一个函数(假设类型匹配,这样我们就可以专注于每个组合的结果,而不是验证每个组合类型检查。)
因此
reverse . map reverse
是一个函数,- 所以
reverse . map reverse . inits
是一个函数, - 和
reverse . map reverse . inits . reverse
是一个函数。
由于tails
被赋予了reverse . map reverse . inits . reverse
的值,tails
本身也是一个函数