Изучай Haskell во имя добра! (Липовача) - страница 176

, имя будет отражать результат, к которому уже применена функция >reverse.



Действие ввода-вывода >fmap (++"!") getLine ведёт себя в точности как функция >getLine, за исключением того, что к её результату всегда добавляется строка >"!" в конец!

Если бы функция >fmap работала только с типом >IO, она имела бы тип >fmap :: (a –> b) –> IO a –> IO b. Функция >fmap принимает функцию и действие ввода-вывода и возвращает новое действие ввода-вывода, похожее на старое, за исключением того, что к результату, содержащемуся в нём, применяется функция.

Предположим, вы связываете результат действия ввода-вывода с именем лишь для того, чтобы применить к нему функцию, а затем даёте очередному результату какое-то другое имя, – в таком случае подумайте над использованием функции >fmap. Если вы хотите применить несколько функций к некоторым данным внутри функтора, то можете объявить свою функцию на верхнем уровне, создать анонимную функцию или, в идеале, использовать композицию функций:

>import Data.Char

>import Data.List


>main = do

>   line <– fmap (intersperse '-' . reverse . map toUpper) getLine

>   putStrLn line

Вот что произойдёт, если мы сохраним этот код в файле fmapping_io.hs, скомпилируем, запустим и введём >"Эй, привет":

>$ ./fmapping_io

>Эй, привет

>Т-Е-В-И-Р-П- -,-Й-Э

Выражение >intersperse '-' . reverse . map toUpper берёт строку, отображает её с помощью функции >toUpper, применяет функцию >reverse к этому результату, а затем применяет к нему выражение >intersperse '-'. Это более красивый способ записи следующего кода:

>(\xs –> intersperse '-' (reverse (map toUpper xs)))

Функции в качестве функторов

Другим экземпляром класса >Functor, с которым мы всё время имели дело, является >(–>) r. Стойте!.. Что, чёрт возьми, означает >(–>) r? Тип функции >r –> a может быть переписан в виде >(–>) r a, так же как мы можем записать >2 + 3 в виде >(+) 2 3. Когда мы воспринимаем его как >(–>) r a, то >(–>) представляется немного в другом свете. Это просто конструктор типа, который принимает два параметра типа, как это делает конструктор >Either.

Но вспомните, что конструктор типа должен принимать в точности один параметр типа, чтобы его можно было сделать экземпляром класса >Functor. Вот почему нельзя сделать конструктор >(–>) экземпляром класса >Functor; однако, если частично применить его до >(–>) r, это не составит никаких проблем. Если бы синтаксис позволял частично применять конструкторы типов с помощью сечений – подобно тому как можно частично применить оператор >+, выполнив >(2+), что равнозначно >(+)>2, – вы могли бы записать >(–>)