Конечно, почти все можно сделать бессмысленным. Сложность в том, какие функции вы разрешите в результирующем выражении. Если мы выполняем сопоставление с образцом, нам обычно нужна функция сворачивания для сопоставления. Так, например, если мы сопоставили шаблон с Maybe a
, нам нужно будет заменить его на maybe
. Точно так же шаблоны Either a b
могут быть записаны в виде either
.
Обратите внимание, что шаблон в подписях
data Maybe a = Nothing | Just a
maybe :: b -> (a -> b) -> (Maybe a -> b)
Maybe a
имеет два конструктора, один не принимает аргументов, а другой принимает a
. Итак, maybe
принимает два аргумента: один является нулевой функцией (b
), а другой принимает a
(a -> b
), а затем возвращает функцию из Maybe a -> b
. Такой же шаблон присутствует в either
data Either a b = Left a | Right b
either :: (a -> c) -> (b -> c) -> (Either a b -> c)
двух случаях. Первый принимает a
и производит все, что c
мы хотим. Второй принимает b
и производит все, что c
мы хотим. В каждом случае нам нужна одна функция для каждого возможного члена в типе суммы.
Чтобы систематически освобождать от точек функцию вроде \[x] -> x
, нам понадобится аналогичная свертка. [a]
объявлен как, по сути,
data [a] = [] | a : [a]
Поэтому нам понадобится функция с этой сигнатурой
list :: b -> (a -> [a] -> b) -> ([a] -> b)
Теперь flip foldr
приближается
flip foldr :: b -> (a -> b -> b) -> ([a] -> b)
Но это рекурсивно. Он вызывает предоставленную ему функцию в [a]
части a : [a]
. Мы хотим истинного сворачивания, которого нет в базовых библиотеках Haskell. Быстрый поиск в Google говорит нам, что эта функция существует в пакете под названием extra
. Конечно, для этого небольшого примера мы можем просто очень легко написать его сами.
list :: b -> (a -> [a] -> b) -> ([a] -> b)
list f g x = case x of
[] -> f
(y:ys) -> g y ys
Теперь мы можем легко применить его к вашему \[x] -> x
. Во-первых, давайте напишем, что на самом деле делает ваша функция, включая все беспорядочные undefined
случаи (здесь я буду использовать undefined
вместо длинного сообщения об ошибке, для краткости)
func :: [a] -> a
func x = case x of
[] -> undefined
(y:ys) -> case ys of
[] -> y
(_:_) -> undefined
Теперь каждый случай оператор точно соответствует каждому конструктору один раз. Это созрело для преобразования в складку.
func :: [a] -> a
func x = case x of
[] -> undefined
(y:ys) -> list y undefined ys
А теперь трансформируем и внешний корпус
func :: [a] -> a
func x = list undefined (\y -> list y undefined) x
Итак, у нас есть
func :: [a] -> a
func = list undefined (\y -> list y undefined)
Или, если мы хотим быть по-настоящему без ума от этого
func :: [a] -> a
func = list undefined (flip list undefined)
Но этой функции нет в базе
Да, это правда. Мы как бы обманули, используя фолд, которого не было. Если мы хотим делать это систематически, нам нужен этот оператор сворачивания. Но без него мы все равно можем объединить его вместе с foldr1
, что достаточно для наших конкретных целей.
func' :: [a] -> a
func' = foldr1 (const (const undefined))
Итак, чтобы ответить на ваш вопрос, мы не всегда можем систематически заменять сопоставление с образцом, как в вашем пример с pointfree, , если у нас нет функции сворачивания с правильной подписью. К счастью, эту функцию всегда можно написать для любого типа данных Haskell 98 (возможно, также и для GADT, но я не рассматривал эту возможность сколько-нибудь подробно). Но даже без этой поддержки мы все равно можем заставить его работать, вроде как.