2015-07-05 3 views
1

Я пробовал бесчисленные поисковые запросы Google, но, будучи довольно новым для Haskell, я не понял половину вещей, которые я нашел, а другая половина просто не совсем соответствовала.Haskell не может вывести тип?

Моя проблема заключается в следующем, если я бегу эти заявления в GHCI

Prelude> let x = 5 :: (Num a) => a 
Prelude> sqrt x 

я получаю то, что я ожидал

2.23606797749979 

Но, если бы это в файл и скомпилировать (должное то, что я делаю здесь довольно тривиально)

sqrtNum :: (Num a, Floating b) => a -> b 
sqrtNum x = sqrt x 

Я получаю эту

myfile.hs:2:18: 
    Could not deduce (a ~ b) 
    from the context (Num a, Floating b) 
     bound by the type signature for 
       sqrtNum :: (Num a, Floating b) => a -> b 
     at test2.hs:1:12-40 
     `a' is a rigid type variable bound by 
      the type signature for sqrtNum :: (Num a, Floating b) => a -> b 
      at test2.hs:1:12 
     `b' is a rigid type variable bound by 
      the type signature for sqrtNum :: (Num a, Floating b) => a -> b 
      at test2.hs:1:12 
    Relevant bindings include 
     x :: a (bound at test2.hs:2:9) 
     sqrtNum :: a -> b (bound at test2.hs:2:1) 
    In the first argument of `sqrt', namely `x' 
    In the expression: sqrt x 

Проблема, вероятно, очень проста, и я просто наблюдаю за ней (как таковой мой опыт с каждой другой ошибкой, с которой я сталкиваюсь), но этот просто не щелкает.

Заранее благодарен!

+1

Ваш тип утверждает, что вы можете конвертировать из одного типа чисел 'a' в другой тип' b'. Но 'sqrt' не делает этого. –

ответ

7

Что вы говорите, у вас есть функция, идущая от Num a to Floating b. Функция sqrt требует ввода типа Floating, но вы только гарантируете Num. Вот тип SQRT:

Main> :t sqrt 
sqrt :: Floating a => a -> a 

Итак, давайте скопируем SQRT и сделать это FTN с плавающей Плавающая:

sqrtNum :: (Num a, Floating a) => a -> a 
sqrtNum x = sqrt x 

Я оставил Num, хотя только Плавающий требуется. Floating is a Fractional, and Fractional is a Num.

3

Если это было недостаточно ясно, давайте посмотрим «под капот». Типовые классы - это форма синтаксического сахара, где Haskell хранит библиотеку функций на уровне типа. Таким образом, ограничения типов могут быть реализованы просто как дополнительные аргументы, которые передаются неявно. Например, если я пишу:

class Truthy a where 
    truthy :: a -> Bool 

instance Truthy Integer where 
    truthy = (== 0) 

instance Truthy Bool where 
    truthy = id 

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

maybeTruthy :: Truthy x => x -> Maybe x 
maybeTruthy x | truthy x = Just x 
       | otherwise = Nothing 

Ключевой момент здесь является то, что я делаю точно так же, как это немного более запутанным кусок кода:

data Truthifier x = Truthifier {truthify :: x -> Bool} 

boolTruthifier :: Truthifier Bool 
boolTruthifier = Truthifier id 

integerTruthifier :: Truthifier Integer 
integerTruthifier = Truthifier (== 0) 

maybeTruthified :: Truthifier x -> x -> Maybe x 
maybeTruthified lib x | truthify lib x = Just x 
         | otherwise  = Nothing 

Единственное отличие состоит в том, что Haskell фактически связаны, например, boolTruthifier к Bool типа, так что мне не нужно, чтобы передать его явно maybeTruthy, потому что он «подходит для езды» через тип Bool аргумента функции. Выше я мог легко определить notTruthifier = Truthifier not, и я мог бы начать делать что-то с maybeTruthified notTruthifier вместо maybeTruthified boolTruthifier.

Это объясняет, почему, например, Haskell по умолчанию запрещает instance (Num a) => Truthy a, и вы должны вместо этого написать newtype TruthNum x = TruthNum x, а затем instance (Num a) => Truthy (TruthNum a): Haskell должен иметь тип верхнего уровня, чтобы зарегистрировать эту библиотеку функций с; если вы дадите ему (Num a) => Truthy a, тогда у него нет лучшего места, чем на a, то есть на всех функциях, тогда как в последнем случае он ставит его непосредственно на тип TruthNum и использует его только тогда, когда x также имеет словарь для функций Num.

Как sqrt вписывается во все это: в GHCi вы узнаете, что тип sqrt является:

Prelude> :t sqrt 
sqrt :: Floating a => a -> a 

Другими словами, sqrt может быть сделано по значению любого типа, если этот тип имеет словарь Floating. Его sqrt возвращает значение того же типа, так как подпись a -> a.

Вы пытались написать (Num a, Floating b) => a -> b. Это более разрешительный тип, чем позволяет на самом деле определение.

Смежные вопросы