2012-04-29 5 views
13

Вопрос 1Haskell: не понимая «Нет инстанс» сообщений об ошибках в GHCI

Привет, если в WinGHCi я намеренно сделать следующий неправильный фрагмент кода:

3 4 

Тогда сообщение об ошибке I get is

<interactive>:1:1: 
    No instance for (Num (a0 -> t0)) 
     arising from the literal `3' 
    Possible fix: add an instance declaration for (Num (a0 -> t0)) 
    In the expression: 3 
    In the expression: 3 4 
    In an equation for `it': it = 3 4 

Что именно означает No instance for (Num (a0 -> t0))?

Вопрос 2

Почему следующий фрагмент кода:

(+) 2 3 4 
<interactive>:1:7: 
    No instance for (Num (a0 -> t0)) 
     arising from the literal `3' 
    Possible fix: add an instance declaration for (Num (a0 -> t0)) 
    In the second argument of `(+)', namely `3' 
    In the expression: (+) 2 3 4 
    In an equation for `it': it = (+) 2 3 4 

выход немного другая ошибка от второй части кода:

2+3 4 
<interactive>:1:3: 
    No instance for (Num (a1 -> a0)) 
     arising from the literal `3' 
    Possible fix: add an instance declaration for (Num (a1 -> a0)) 
    In the expression: 3 
    In the second argument of `(+)', namely `3 4' 
    In the expression: 2 + 3 4 

именно в первом кусок кода у нас есть No instance for (Num (a0 -> t0)), где, как и во втором фрагменте кода, есть No instance for (Num (a1 -> a0)).


[Ответ на ehird]

(Вопросы переехал из комментариев ответа):

1) Я понимаю, что последние два выражения различны, но ты говоришь, что я не должен пытаться понять почему переводчик выбирает (Num (a0 -> t0)) для первого и (Num(a1 -> a0)) для последнего, кроме того, что они разные?

2) Привет, а с первым, когда вы говорите «Но нет экземпляра Num для функций», что вы имеете в виду? Извините, я не понимаю, что такое понятие экземпляра. Кроме того, просто из любопытства, можете ли вы использовать свой пример Num (a -> b), чтобы как-то сказать интерпретатору интерпретировать 3 4 как 4 modulo 3?

ответ

16

Мое намерение состоит в дополнении ответа ehird с более подробным объяснением.Когда вы написали выражение

3 4 

Затем интерпретатор Haskell думает, что вы пытаетесь применить функцию 3 к любому 4 есть. Для того, чтобы Haskell интерпретировать 3 как функцию, она должна сделать вызов функции

fromInteger :: Integer -> (a -> b) 

для того, чтобы получить функцию (то есть что-то типа a -> b) от целого числа 3. Теперь fromInteger определяется в Num класса типов, чтобы иметь подпись

instance Num x where 
    fromInteger :: Integer -> x 

т.е. когда вы делаете тип x Экземпляр Num класса, вы даете реализацию fromInteger, который говорит Haskell, как преобразовать целое число буквального в a x. В вашем случае x - это тип функции a -> b. Так что давайте сделаем это!


Во-первых, некоторые шаблоны. Для того, чтобы x экземпляр Num Haskell требует, чтобы мы также сделать его экземпляр Show и Eq:

instance Show (a -> b) where show f = "<function>" 
instance Eq (a -> b) where f == g = False 

Теперь предположим, что мы хотим интерпретировать 3 4 как «4 по модулю 3». Затем нам нужно сообщить Haskell, как интерпретировать любое целое число как функцию, которая вызывает mod. Кроме того, поскольку mod принимает только целые типы (она имеет подпись mod :: Integral a => a -> a -> a), то необходимо ограничить типы a и b быть неотъемлемой, а также:

instance (Integral a, Integral b) => Num (a -> b) where 

Для make an instance of Num мы должны дать реализации (+), (-) , (*) и fromIntegral (на самом деле мы должны также определить пару других функций, но давайте не будем беспокоиться об этом сейчас).

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

f + g = \x -> f x + g x 
    f - g = \x -> f x - g x 
    f * g = \x -> f x * g x 

т.е. при добавлении двух функций f и g, вы получаете новую функцию, которая применяет как аргументы f, так и g, а затем добавляет их вместе. Поскольку нам требовалось, чтобы результат применения f и g был интегрального типа, мы знаем, что имеет смысл добавить свои выходы.

Интерпретировать целое число в качестве функции мы можем написать

fromInteger n = \m -> fromIntegral m `mod` fromIntegral n 

т.е. когда мы имеем целое n, мы возвращаем функцию параметра m, что при вызове, гарантирует, что оба аргумента имеют тот же (путем вызова fromIntegral на оба из них), а затем использует их в качестве аргументов функции mod.

Наконец, немного больше шаблоннога, чтобы остановить Haskell жалуется:

abs f = undefined 
    signum f = undefined 

Мы можем проверить это. У меня есть код в файле numfun.hs. Я загрузится интерпретатор Haskell и загрузить мой файл:

Prelude> :l numfun.hs 
[1 of 1] Compiling Main    (numfun.hs, interpreted) 
Ok, modules loaded: Main. 

Теперь я могу определить некоторые функции:

*Main> let f = (+ 1) 
*Main> let g = (* 2) 

я могу добавить их или вычесть их:

*Main> (f + g) 3 -- (3+1) + (3*2) 
10 
*Main> (f - g) 3 -- (3+1) - (3*2) 
-2 

И я могу номера телефонов как функции:

*Main> 3 4   -- 4 `mod` 3 
1 
+0

Wow большое спасибо за это подробное и хорошо изложенное объяснение; Я очень ценю это. Я думаю, мне нужно будет ударить по некоторым книгам, указанным на веб-сайте Haskell, и вернуться к вашему сообщению еще несколько раз, прежде чем я перевариваю все, что вы написали. Спасибо. – artella

14

Первая ошибка возникает из-за того, что целочисленный литерал, такой как 4, может быть любого типа с экземпляром Num. То есть, 4 имеет тип (Num a) => a, поэтому он может служить в качестве Integer, в Double, в Rational и т.д. Так как Вы обращались 3 к аргументу (4), он знает, что в контексте 3 должен быть тип функции (т.е. a0 -> t0 для некоторых a0 и t0). Но для функций нет Num, поэтому использование функции 3 недействительно. Если вы добавили instance Num (a -> b), это сработает, но вы, вероятно, этого не захотите.

Что касается последнего, то два сообщения об ошибках эквивалентны; имена, созданные GHC, не имеют особого значения. Буквы обычно выводятся из переменных типа в типах функций, которые вы используете, и числа добавляются, чтобы держать вещи недвусмысленными. В этом случае второе выражение эквивалентно (+) 2 (3 4) (поскольку функциональное приложение связывается более жестко, чем любой инфиксный оператор), что не совсем так же, как ваш первый фрагмент кода.

+0

привет, я ценю вы должны сказать, что я не должен пытаться понять, почему интерпретатор выбирает '(Num (a0 -> t0))' для первого и '(Num (a1 -> a0))' для последнее, кроме того, что они разные? Благодарю. – artella

+0

Привет, а с первым, когда вы говорите «Но нет экземпляра Num для функций», что вы имеете в виду? Извините, я не понимаю, что такое понятие экземпляра. Кроме того, просто из любопытства вы могли бы использовать метод 'instance Num (a -> b)', чтобы каким-то образом интерпретировать интерпретатор '3 4' как '4 modulo 3'? Спасибо – artella