Выбрать главу

Prelude> (1::Int) + (1::Double)

< interactive>:2:13:

Couldn’t match expected type Int’ with actual type Double’

In the second argument of ‘(+)’, namely ‘(1 :: Double)’

In the expression: (1 :: Int) + (1 :: Double)

In an equation for ‘it’: it = (1 :: Int) + (1 :: Double)

Любое преобразование типов контролируется пользователем. Мы должны вызвать специальную функ-

цию.

От целых к действительным: Часто возникает необходимость приведения целых чисел к действитель-

ным при делении. Для этого можно воспользоваться функцией: fromIntegral

Prelude> :i fromIntegral

fromIntegral :: (Integral a, Num b) => a -> b

-- Defined in ‘GHC.Real’

Определим функцию поиска среднего между двумя целыми числами:

meanInt :: Int -> Int -> Double

meanInt a b = fromIntegral (a + b) / 2

Арифметика | 35

В этой функции двойка имеет тип Double. Обратите внимание на скобки: составной синоним всегда при-

тягивает аргументы сильнее чем бинарная операция.

От действительных к целым: В этом нам поможет класс RealFrac. Методы говорят сами за себя:

Prelude GHC.Float> :i RealFrac

class (Real a, Fractional a) => RealFrac a where

properFraction :: Integral b => a -> (b, a)

truncate :: Integral b => a -> b

round :: Integral b => a -> b

ceiling :: Integral b => a -> b

floor :: Integral b => a -> b

-- Defined in ‘GHC.Real’

instance RealFrac Float -- Defined in ‘GHC.Float’

instance RealFrac Double -- Defined in ‘GHC.Float’

Метод properFraction отделяет целую часть числа от дробной:

properFraction :: Integral b => a -> (b, a)

Для того, чтобы вернуть сразу два значения используется кортеж (кортежи пишутся в обычных скобках,

значения следуют через запятую):

Prelude> properFraction 2.5

(2,0.5)

Для пар (кортеж, состоящий из двух элементов) определены две удобные функции извлечения элементов,

их смысл можно понять по одним лишь типам:

fst :: (a, b) -> a

snd :: (a, b) -> b

Проверим:

Prelude> let x = properFraction 2.5

Prelude> (fst x, snd x)

(2, 0.5)

Мы бы и сами могли определить такие функции:

fst :: (a, b) -> a

fst (a, _) = a

snd :: (a, b) -> b

snd (_, b) = b

Между действительными числами: Кто-то написал очень хорошую функцию, но она определена на

Double, а вам приходится использовать Float. Как быть? Нам поможет функция realToFrac:

Prelude> :i realToFrac

realToFrac :: (Real a, Fractional b) => a -> b

-- Defined in ‘GHC.Real’

Она принимает значение из класса Real и приводит его к значению, которое можно делить. Что это за

класс Real? Математики наверное смекнут, что это противоположность комплексным числам (где-то должен

быть определён тип или класс Complex, и он правда есть, но об этом в следующем разделе). При переходе

к комплексным числам мы теряем способность сравнения на больше/меньше, но сохраняем возможность

вычисления арифметических операций, поэтому класс Real это пересечение классов Num и Ord:

Prelude> :i Real

class (Num a, Ord a) => Real a where

toRational :: a -> Rational

Здесь “пересечение” означает “и тот и другой”. Пересечение классов кодируется с помощью контекста.

Вернёмся к нашему первому примеру:

36 | Глава 2: Первая программа

Prelude> realToFrac (1::Float) + (1::Double)

2.0

Отметим, что этой функцией можно пользоваться не только для типов Float и Double, в Haskell возможны

самые экзотические числа.

Если преобразования между Float и Double происходят очень-очень часто, возможно имеет смысл вос-

пользоваться специальными для GHC функциями: Они определены в модуле GHC.Float:

Prelude> :m +GHC.Float

Prelude GHC.Float> :t float2Double

float2Double :: Float -> Double

Prelude GHC.Float> :t double2float

double2Float :: Double -> Float

2.7 Документация

К этой главе мы уже рассмотрели основные конструкции языка и базовые типы. Если у вас есть какая-то