问题
I'm trying to understand the following piece of code:
import Data.Char (ord)
encodeInteger :: String -> Integer
encodeInteger = read . concatMap ch
where ch c = show (ord c)
But I don't see how this can work when encodeInteger
is defined as a function that takes a string, but in the second line, the function is implemented without that string argument.
Also, concatMap
(according to hoogle), takes a function and a list, but only the function ch
is provided.
Why does this code still work? Is the argument somehow magically passed? Has it something to do with currying?
edit: And why doesn't it work to change it like this:
encodeInteger :: String -> Integer
encodeInteger a = read . concatMap ch a
where ch c = show (ord c)
回答1:
Basically defining a function
f = g
is the same as defining the function
f x = g x
In your specific case, you can use
encodeInteger a = (read . concatMap ch) a
to define your function. The parentheses are needed, otherwise it is parsed as
encodeInteger a = (read) . (concatMap ch a)
and concatMap ch a
is not a function and can not be composed. At most you could write
encodeInteger a = read (concatMap ch a)
-- or
encodeInteger a = read $ concatMap ch a
About "why concatMap ch
takes only one argument?". This is a partial application, which is very common in Haskell. If you have
f x y z = x+y+z
you can call f
with fewer arguments, and obtain as the result a function of the remaining arguments. E.g., f 1 2
is the function taking z
and returning 1+2+z
.
Concretely, thanks to Currying, there's no such a thing as a function taking two or more arguments. Every function always takes only one argument. When you have a function like
foo :: Int -> Bool -> String
then foo
takes one argument, an Int
. It returns a function, which takes a Bool
and finally returns a String
. You can visualize this by writing
foo :: Int -> (Bool -> String)
Anyway, if you look up currying and partial application, you will find plenty of examples.
回答2:
encodeInteger :: String -> Integer
encodeInteger = read.concatMap (\char -> show $ ord char)
The encodeInteger
on the left hand side (LHS) of "=" is a name; it refers to the function on the right hand side (RHS) of "=". Both have the function type: String -> Integer
. Both take a list of characters and produces an integer. Haskell enables us to express such function equality without specifying formal arguments (a style known as point-free).
Now, let's look at the RHS. The (.) operator composes two functions together. The composed function takes a string as its input from concatMap
and produces an integer coming out of read
as the output of the composed function.
concatMap
itself takes 2 inputs, but we need to leave out the second one for the composed function, which requires a string as its input. We achieve this by partially applying concatMap
, including only its first argument.
来源:https://stackoverflow.com/questions/43253598/why-does-this-function-work-even-though-the-argument-is-missing