Подтвердить что ты не робот

Когда в Haskell нужны подписи типа?

Многие вводные тексты расскажут вам, что подписи типа Haskell "почти всегда" являются необязательными. Может ли кто-нибудь количественно определить "почти" часть?

Насколько я могу судить, единственный раз, когда вам нужна явная подпись, нужно устранить классы классов. (Канонический пример: read . show.) Есть ли другие случаи, о которых я не думал, или это он?

(Я знаю, что если вы выходите за пределы Haskell 2010, существует множество исключений. Например, GHC никогда не выведет типы ранжирования N. Но типы ранга N - это языковое расширение, а не часть официального стандарта [ еще].)

4b9b3361

Ответ 1

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

Если у вас включен MonomorphismRestriction, иногда вам нужно добавить подпись типа, чтобы получить наиболее общий тип:

{-# LANGUAGE MonomorphismRestriction #-}
-- myPrint :: Show a => a -> IO ()
myPrint = print
main = do
  myPrint ()
  myPrint "hello"

Это не будет выполнено, потому что myPrint является мономорфным. Вам нужно будет раскомментировать подпись типа, чтобы он работал, или отключить MonomorphismRestriction.

Phantom ограничения

Когда вы помещаете полиморфное значение с ограничением в кортеж, сам кортеж становится полиморфным и имеет такое же ограничение:

myValue :: Read a => a
myValue = read "0"

myTuple :: Read a => (a, String)
myTuple = (myValue, "hello")

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

myString = snd myTuple

Хотя интуитивно можно было бы ожидать, что myString будет всего лишь String, проверяющий тип должен специфицировать переменную типа a и выяснить, действительно ли ограничение выполнено. Чтобы это выражение работало, нужно было бы аннотировать тип либо snd, либо myTuple:

myString = snd (myTuple :: ((), String))

Ответ 2

Полиморфная рекурсия требует, вообще говоря, аннотации типов.

f :: (a -> a) -> (a -> b) -> Int -> a -> b
f f1 g n x = 
    if n == (0 :: Int)
    then g x
    else f f1 (\z h -> g (h z)) (n-1) x f1

(Кредит: Патрик Кусо)

Обратите внимание, что рекурсивный вызов выглядит плохо напечатанным (!): он вызывает себя с пятью аргументами, несмотря на то, что f имеет только четыре! Затем помните, что b может быть создан с помощью c -> d, что вызывает появление дополнительного аргумента.

Вышеприведенный пример вычисляет

f f1 g n x = g (f1 (f1 (f1 ... (f1 x))))

где f1 применяется n раз. Конечно, есть гораздо более простой способ написать эквивалентную программу.

Ответ 3

В Haskell, как я уверен, вы знаете, типы выводятся. Другими словами, компилятор определяет, какой тип вы хотите.

Однако в Haskell существуют также полиморфные классы типов с функциями, которые действуют по-разному в зависимости от типа возврата. Вот пример класса Monad, хотя я не определил все:

class Monad m where
    return :: a -> m a
    (>>=) :: m a -> (a -> m b) -> m b
    fail :: String -> m a

Нам дается множество функций с типом сигнатур. Наша задача состоит в том, чтобы сделать объявления экземпляров для разных типов, которые можно рассматривать как Monads, например Maybe t или [t].

Посмотрите на этот код - он не будет работать так, как мы могли ожидать:

return 7

Это функция из класса Monad, но поскольку существует более одной Monad, мы должны указать, какое возвращаемое значение/тип мы хотим, или оно автоматически становится IO Monad. Итак:

return 7 :: Maybe Int
-- Will return...
Just 7

return 6 :: [Int]
-- Will return...
[6]

Это связано с тем, что [t] и Maybe оба были определены в классе типа Monad.

Вот еще один пример, на этот раз из случайного типа. Этот код вызывает ошибку:

random (mkStdGen 100)

Поскольку random возвращает что-то в классе random, нам нужно будет определить, какой тип мы хотим вернуть, с tepelo объекта StdGen с любым желаемым значением:

random (mkStdGen 100) :: (Int, StdGen)
-- Returns...
(-3650871090684229393,693699796 2103410263)

random (mkStdGen 100) :: (Bool, StdGen)
-- Returns...
(True,4041414 40692)

Все это можно найти в узнать вас в Haskell, хотя вам придется долго читать. Это, я на 100% уверен, это единственный раз, когда типы необходимы.