我一直在玩haskell,我发现如果我在代码文件中编写以下函数:
f :: Int -> [a] -> a
f idx str = last $ (take . succ) idx str
然后这完全没问题。当然,我认为没有参数,代码看起来会更好。
f :: Int -> [a] -> a
f = last $ (take . succ)
但是当我尝试将其加载到gchi中时会产生错误
Couldn't match expected type `[a]'
against inferred type `Int -> [a1] -> [a1]'
In the second argument of `($)', namely `(take . succ)'
In the expression: last $ (take . succ)
In the definition of `f': f = last $ (take . succ)
失败,模块加载:无。
我对如何发生这种情况感到困惑......
答案 0 :(得分:10)
你误解了优先权。这样:
f idx str = last $ (take . succ) idx str
解析如下:
f idx str = last $ ( (take . succ) idx str )
不是(如您所愿)这样:
f idx str = ( last $ (take . succ) ) idx str
$
具有极高的任何运算符的最低优先级,函数调用具有极高的优先级。 .
具有第二高,因此(take . succ)
在绑定到idx str
之前绑定它的参数(last $
)。
此外,该函数(编译时)不会像您希望的那样执行。它递增idx
,然后从字符串中获取该字符。如果这是您想要的,为什么在succ
工作时使用(+1)
?您已经将类型限制为整数。
如上所述,您的函数与!!
运算符相同 - 它只是一个数组索引函数。这是你想要的吗?或者您想要succ
给定索引处的项目?您可以通过以下方式实现这一目标:
f :: Enum a => Int -> [a] -> a
f idx str = succ $ str !! idx
-- or
f idx str = succ $ (!!) str idx
-- or, only one argument
f idx = succ . (!! idx)
我还在研究一个没有书面论据的版本。编写工作代码可能更重要吗? ;)
答案 1 :(得分:6)
当您尝试使用last
(take . succ)
时会发生这种情况
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
last :: [t] -> t ~ (b -> c)
-- so b ~ [t] and c ~ t
(take . succ) :: Int -> [t] -> [t]
-- so a ~ int and b ~ [t] -> [t]
b的类型被推断为来自[t]
的{{1}},但它与last
中(take . succ)
答案 2 :(得分:5)
f idx str = last $ (take . succ) idx str
-- applying definition of ($)
f idx str = last ((take . succ) idx str)
-- adding parentheses for clarity
f idx str = last (((take . succ) idx) str)
-- using definition of (.)
f idx str = (last . (take . succ) idx) str
-- η-conversion
f idx = last . (take . succ) idx
-- infix to prefix notation
f idx = (.) last ((take . succ) idx)
-- ading parentheses for clarity
f idx = ((.) last) ((take . succ) idx)
-- using definition of (.)
f idx = ((.) last . (take . succ)) idx
-- η-conversion
f = (.) last . (take . succ)
-- remove parentheses: (.) is right-associative
f = (.) last . take . succ