Why does this function work even though the argument is missing?

痴心易碎 提交于 2020-01-24 11:04:06

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!