Хорошо, давайте посмотрим на сигнатуру типа для функции curry foldr
:
>:t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
То есть foldr
принимает двоичную функцию (т.е. a->b->b
), значение b
, список значений a
и возвращает значение b
.
Давайте также посмотрим документацию для foldr
, чтобы получить более четкое определение:
foldr, применяется к бинарному оператору, начальное значение (обычно
тождество оператора), а список сокращает список, используя
бинарный оператор справа налево:
Теперь давайте посмотрим на сигнатуру типа для myConcat xs = foldr (++) []
> :t myConcat
myConcat :: t -> [[a]] -> [a]
Хм ... это не то, что мы хотели ...
Проблема в том, что вы никогда не указали foldr
значение типа [a]
. Итак, теперь myConcat
нужно какое-то значение любого типа, чтобы удовлетворить xs
и значение типа [a]
для завершения foldr (++) []
, например:
> myConcat 2 [[1,2],[3,4]]
[1,2,3,4]
> myConcat Nothing [[1,2],[3,4]]
[1,2,3,4]
Это работает, но первый аргумент - просто пустая трата.
Однако, если мы передадим это xs
значение foldr (++) []
, например:
myConcat xs = foldr (++) [] xs
и проверьте тип подписи
> :t myConcat
myConcat :: [[a]] -> [a]
Ах, намного лучше. Теперь myConcat использует xs
для завершения функции foldr
.
Кроме того, myConcat = foldr (++) []
также работает и фактически является примером бессмысленного программирования в стиле . Если мы проверим тип подписи foldr (++) []
,
> :t foldr (++) []
foldr (++) [] :: [[a]] -> [a]
Поскольку мы уже предоставили foldr
его первые два аргумента через частичное приложение , мы возвращаем функцию, которая принимает значение [[a]]
и делает то, что мы хотим! Поэтому мы просто присваиваем его имени, и оно работает так же, как в примере выше, но нам не нужно явно передавать аргументы!
> let myConcat = foldr (++) []
> :t myConcat
myConcat :: [[a]] -> [a]
> myConcat [[1,2],[3,4]]
[1,2,3,4]