52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 53

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 53

тор не сможет определить для какого типа конкретно мы хотим выполнить применение. Мы будем называть

такую ситуацию неопределённостью:

x :: T a => a

f :: C a => a -> b

f x :: ??

-- неопределённость

Мы видим, что тип x, это какой-то тип, одновременно принадлежащий и классу T и классу C. Но мы не

можем сказать какой это тип. У этого поведения есть исключение: по умолчанию числа приводятся к Integer,

если они не содержат знаков после точки, и к Double – если содержат.

*Nat Prelude> let f = (1.5 + )

*Nat Prelude> :t f

f :: Double -> Double

*Nat Prelude> let x = 5 + 0

*Nat Prelude> :t x

x :: Integer

*Nat Prelude> let x = 5 + Zero

*Nat Prelude> :t x

x :: Nat

Умолчания определены только для класса Num. Для этого есть специальное ключевое слово default. В

рамках модуля мы можем указать какие типы считаются числами по умолчанию. Например, так (такое умол-

чание действует в каждом модуле, но мы можем переопределить его):

default (Integer, Double)

Работает правило: если произошла неопределённость и один из участвующих классов является Num, а все

остальные классы – это стандартные классы, определённые в Prelude, то компилятор начинает последова-

тельно пробовать все типы, перечисленые за ключевым словом default, пока один из них не подойдёт. Если

такого типа не окажется, компилятор скажет об ошибке.

Ограничение мономорфизма

С выводом типов в классах связана одна тонкость. Мы говорили, что не обязательно выписывать типы

выражений, компилятор может вывести их самостоятельно. Например, мы постоянно пользуемся этим в ин-

терпретаторе. Также когда мы говорили о частичном применении, мы сказали об очень полезном умолчании

в типах функций. О том, что за счёт частичного применения, все функции являются функциями одного аргу-

мента. Эта особенность позволяет записывать выражения очень кратко. Но иногда они получаются чересчур

краткими, и вводят компилятор в заблуждение. Зайдём в интерпретатор:

Prelude> let add = (+)

Prelude> :t add

add :: Integer -> Integer -> Integer

Мы хотели определить синоним для метода плюс из класса Num, но вместо ожидаемого общего типа

получили более частный. Сработало умолчание для численного типа. Но зачем оно сработало? Если мы

попробуем дать синоним методу из класса Eq, ситуация станет ещё более странной:

Prelude> let eq = (==)

Prelude> :t eq

eq :: () -> () -> Bool

Мы получили какую-то ерунду. Если мы попытаемся загрузить модуль с этими определениями:

52 | Глава 3: Типы

module MR where

add = (+)

eq

= (==)

то получим:

*MR> :l MR