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

. Сорт – это нечто вроде «типа типов». Звучит немного странно, но на самом деле это очень мощная концепция.

Что такое сорта и для чего они полезны? Давайте посмотрим сорт типа, используя команду >:k в интерпретаторе GHCi.

>ghci> :k Int

>Int :: *

Звёздочка? Как затейливо! Что это значит? Звёздочка обозначает, что тип является конкретным. Конкретный тип – это такой тип, у которого нет типов-параметров; значения могут быть только конкретных типов. Если бы мне надо было прочитать символ >* вслух (до этого не приходилось), я бы сказал «звёздочка» или просто «тип».

О’кей, теперь посмотрим, каков сорт у типа >Maybe:

>ghci> :k Maybe

>Maybe :: * –> *

Конструктор типов >Maybe принимает один конкретный тип (например, >Int) и возвращает конкретный тип (например, >Maybe Int). Вот о чём говорит нам сорт. Точно так же тип >Int –> Int означает, что функция принимает и возвращает значение типа >Int; сорт >* – > * означает, что конструктор типов принимает конкретный тип и возвращает конкретный тип. Давайте применим параметр к типу >Maybe и посмотрим, какого он станет сорта.

>ghci> :k Maybe Int

>Maybe Int :: *

Так я и думал! Мы применили тип-параметр к типу >Maybe и получили конкретный тип. Можно провести параллель (но не отождествление: типы – это не то же самое, что и сорта) с тем, как если бы мы сделали >:t isUpper и >:t isUpper 'A'. У функции >isUpper тип >Char –> Bool; выражение >isUpper 'A' имеет тип >Bool, потому что его значение – просто >False. Сорт обоих типов, тем не менее, >*.

Мы используем команду >:k для типов, чтобы получить их сорт, так же как используем команду >:t для значений, чтобы получить их тип. Выше уже было сказано, что типы – это метки значений, а сорта – это метки типов; и в этом они схожи.

Посмотрим на другие сорта.

>ghci> :k Either

>Either :: * –> * –> *

Это говорит о том, что тип >Either принимает два конкретных типа для того, чтобы вернуть конкретный тип. Выглядит как декларация функции, которая принимает два значения и что-то возвращает. Конструкторы типов являются каррированными (так же, как и функции), поэтому мы можем частично применять их.

>ghci> :k Either String

>Either String :: * –> *

>ghci> :k Either String Int

>Either String Int :: *

Когда нам нужно было сделать для типа >Either экземпляр класса >Functor, пришлось частично применить его, потому что класс >Functor принимает типы только с одним параметром, в то время как у типа >Either их два. Другими словами, класс >Functor принимает типы сорта >* –> *, и нам пришлось частично применить тип >Either для того, чтобы получить сорт >* –> * из исходного сорта