我目前正在尝试学习Haskell,但我正在努力理解语法。例如,取map
函数:
map :: (s -> t) -> [s] -> [t]
map f [] = []
map f (x:xs) = f x : map f xs
我理解该函数的作用,map
有一个函数f :: s -> t
作为参数。但我读map :: (s -> t) -> [s] -> [t]
为“map是一个函数,它将函数映射从s映射到s再映射到s然后映射到t”,这显然是错误的。有人能帮我解决这个问题吗?
答案 0 :(得分:17)
类型(s -> t) -> [s] -> [t]
可以通过两种方式阅读。一种方法是将其视为两个参数的函数,第一个是类型s -> t
的函数,第二个是类型[s]
的列表。返回值的类型为[t]
。
另一种方法是理解函数箭头是右关联的,因此类型等同于(s -> t) -> ([s] -> [t])
。根据这种解释,map
是一个函数,它将一个函数从元素接收到元素s -> t
,并将其转换为列表到列表[s] -> [t]
的函数。
同样,在使用该函数时,您可以将map foo xs
视为将函数map
应用于两个参数foo
和xs
。或者,由于函数应用程序 left -associative,您可以将其视为(map foo) xs
,将map
应用于单个参数foo
以获取新函数然后将其应用于xs
。
由于Haskell函数是curried,所以这只是看待完全相同的两种方式。
答案 1 :(得分:2)
定义几个类型的别名可能会有所帮助,使它更明确地表示所有这些箭头和括号正在做什么:
type F1 a b = a -> b -- type synonym for single-argument functions
type List a = [a] -- type synonym for lists
现在您可以将map
的类型签名写为:
map :: F1 s t -> List s -> List t
如果你对Java或C ++或其他什么更熟悉,那么在语法上看起来更像:
List<T> map(F1<S, T> fun, List<S> list); // applies fun to each element in list
所以你可以这样想:map
接受一个函数和一个列表,然后返回另一个列表。但是,由于函数在Haskell中 curry ,因此您不必一次传递所有参数。您可以将部分应用 map
放到第一个参数中。所以它的类型签名更像是:
F1<List<S>, List<T>> map(F1<S, T> fun); // returns a function that applies fun to each element in a list
...当你只用map
个参数调用fun
时,会给你一些类似的东西:
List<T> mapFun(List<S> list); // applies fun to each element in list
现在回到Haskell:您可以将map :: (s -> t) -> [s] -> [t]
读作:
map
从 s 到 t 的函数和 s 的列表,并返回< EM>吨“map
从 s 到 t 获取一个函数,并将其转换为 列表中的函数一个列表t “前者很好;后者更有帮助。
答案 2 :(得分:1)
“map”是一个函数,它将(函数从s到t)映射到(给出一个(t)列表的s列表中)? 这是将类型签名直接翻译成英文(虽然不是很优雅的英文)。
答案 3 :(得分:1)
从最后阅读签名:-> [t]
表示返回t
列表。其余的是“常规”参数。
因此,map
采用s
生成t
的函数和s
列表。
现在很简单:使用函数s->t
,将其应用于[s]
的每个元素,其结果为[t]
。