. Наиболее часто используемая функция в классе типов >Show
– это, собственно, функция >show
. Она берёт значение, для типа которого определён экземпляр класса >Show
, и представляет его в виде строки.
>ghci> show 3
>"3"
>ghci> show 5.334
>"5.334"
>ghci> show True
>"True"
Класс Read
Класс >Read
– это нечто противоположное классу типов >Show
. Функция >read
принимает строку и возвращает значение, тип которого является экземпляром класса >Read
.
>ghci> read "True" || False
>True
>ghci> read "8.2" + 3.8
>12.0
>ghci> read "5" – 2
>3
>ghci> read "[1,2,3,4]" ++ [3]
>[1,2,3,4,3]
Отлично. Но что случится, если попробовать вызвать >read
>"4"
?
>ghci> read "4"
>
> Ambiguous type variable `a' in the constraint:
> `Read a' arising from a use of `read' at
> Probable fix: add a type signature that fixes these type variable(s)
Интерпретатор GHCi пытается нам сказать, что он не знает, что именно мы хотим получить в результате. Заметьте: во время предыдущих вызовов функции >read
мы что-то делали с результатом функции. Таким образом, интерпретатор GHCi мог вычислить, какой тип ответа из функции >read
мы хотим получить.
Когда мы использовали результат как булево выражение, GHCi «понимал», что надо вернуть значение типа >Bool
. А в данном случае он знает, что нам нужен некий тип, входящий в класс >Read
, но не знает, какой именно. Давайте посмотрим на сигнатуру функции >read
.
>ghci> :t read
>read :: (Read a) => String –> a
ПРИМЕЧАНИЕ. Идентификатор >String
– альтернативное наименование типа >[Char]
. Идентификаторы >String
и >[Char]
могут быть использованы взаимозаменяемо, но далее будет использоваться только >String
, поскольку это удобнее и писать, и читать.
Видите? Функция возвращает тип, имеющий экземпляр класса >Read
, но если мы не воспользуемся им позже, то у компилятора не будет способа определить, какой именно это тип. Вот почему используются явные аннотации типа. Аннотации типа – способ явно указать, какого типа должно быть выражение. Делается это с помощью добавления символов >::
в конец выражения и указания типа. Смотрите:
>ghci> read "5" :: Int
>5
>ghci> read "5" :: Float
>5.0
>ghci> (read "5" :: Float) * 4
>20.0
>ghci> read "[1,2,3,4]" :: [Int]
>[1,2,3,4]
>ghci> read "(3, 'a')" :: (Int, Char)
>(3, 'a')
Для большинства выражений компилятор может вывести тип самостоятельно. Но иногда он не знает, вернуть ли значение типа >Int
или >Float
для выражения вроде >read "5"
. Чтобы узнать, какой у него тип, язык Haskell должен был бы фактически вычислить >read "5"
.
Но так как Haskell – статически типизированный язык, он должен знать все типы до того, как скомпилируется код (или, в случае GHCi, вычислится). Так что мы должны сказать языку: «Эй, это выражение должно иметь вот такой тип, если ты сам случайно не понял!»
Обычно компилятору достаточно минимума информации, чтобы определить, значение какого именно типа должна вернуть функция >read
. Скажем, если результат функции >read
помещается в список, то Haskell использует тип списка, полученный благодаря наличию других элементов списка:
>ghci> [read "True" , False, True, False]
>[True, False, True, False]