Я новичок в Haskell, и у меня есть некоторые проблемы с пониманием этого. У меня есть 2 функции, определенные в таком файле:
expr n x = (x ^ n)/(fact n)
fact n
| n == 0 = 1
| otherwise = n * fact (n - 1)
Но когда я пытаюсь запустить say expr 3 2
, я продолжаю получать такую ошибку:
*Main> expr 3 2
<interactive>:31:1: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘print’
prevents the constraint ‘(Show a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
...plus 22 others
...plus 19 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In a stmt of an interactive GHCi command: print it
Я проверил типы для различных выражений и вижу это:
*Main> :t (/)
(/) :: Fractional a => a -> a -> a
*Main> :t (^)
(^) :: (Integral b, Num a) => a -> b -> a
*Main> :t 3 ^ 6
3 ^ 6 :: Num a => a
*Main> :t fact
fact :: (Eq p, Num p) => p -> p
*Main> :t fact 3
fact 3 :: (Eq p, Num p) => p
*Main> :t 9 / 6
9 / 6 :: Fractional a => a
Но я не могу понять, какие типы дать моим функциям, чтобы избежать этого. Я попытался придать моей функции фактов такой тип, поскольку (/) ожидает Fractional:
fact :: (Fractional a) => Int -> a
Но если я попытаюсь указать свои типы функций, файл даже не загрузится в ghci. Я могу опубликовать сообщение об ошибке, которое я получаю при попытке загрузить мой файл с типами, если это поможет.
Если я попытаюсь добавить тип к показу, используемому при оценке выражения, это тоже не сработает:
*Main> expr 3 2 :: String
<interactive>:37:1: error:
• No instance for (Fractional String) arising from a use of ‘expr’
• In the expression: expr 3 2 :: String
In an equation for ‘it’: it = expr 3 2 :: String
<interactive>:37:6: error:
• No instance for (Num String) arising from the literal ‘3’
• In the first argument of ‘expr’, namely ‘3’
In the expression: expr 3 2 :: String
In an equation for ‘it’: it = expr 3 2 :: String
*Main> expr 3 2 :: Integer
<interactive>:38:1: error:
• No instance for (Fractional Integer) arising from a use of ‘expr’
• In the expression: expr 3 2 :: Integer
In an equation for ‘it’: it = expr 3 2 :: Integer
Я пробовал. Этот гам тоже работает. Я отредактирую свой вопрос, чтобы добавить ответ на него. Я пробовал, Integer, Double, Float.
Вы не можете выбрать какой-либо возвращаемый тип, только те, которые совместимы с ограничениями, накладываемыми используемыми вами операторами.
Да. Вот что я пытаюсь выяснить. Какой здесь должен быть тип, потому что я не очень хорошо разбираюсь в том, как работают типы. Тип, возвращаемый (/), является правильным дробным? Так что мне делать expr 3 2 :: Fractional a
или что-то в этом роде?
В Haskell арифметические операторы, за исключением (^), заставляют два своих операнда иметь один и тот же тип. Поэтому, если вам нужны значения Double, вам просто нужно указать, что знаменатель должен быть типа Double, например, так: expr n x = (x ^ n) / ((fromIntegral (fact n))::Double)
; это вытеснит неоднозначность типа и, таким образом, сделает выражение доступным для отображения.
Haskell выводит правильный тип для вас: :t expr
вернет (Fractional a, Integral a) => a -> a -> a
, потому что использование /
требует, чтобы аргументы имели экземпляр Fractional
, а использование ^
вызывает экземпляр Integral
. Если вы хотите вызвать show
на нем (как это пытается сделать ghci
), a
также должен иметь экземпляр Show
.
Как упоминалось в комментариях, оператор /
, как и для + - *, заставляет два своих операнда иметь один и тот же тип (и это также тип результата). Оператор ^
является исключением, и его результат должен иметь тот же тип, что и его левый операнд. Обратите внимание, что для типов с плавающей запятой у вас также есть оператор возведения в степень **
в стиле Fortran.
Более подробно правила объясняются в этом туториале.
К сожалению, это означает, что смешанную арифметику в Haskell реализовать несколько сложнее, чем в некоторых известных императивных языках. При необходимости можно использовать функцию преобразования fromIntegral :: (Integral a, Num b) => a -> b. Это дает компилятору лицензию на вставку подходящего преобразования.
В данной ситуации, если желательна широкая подпись типа, это может быть достигнуто, например, следующим образом:
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ExplicitForAll #-}
fact :: Integral nt => nt -> nt
fact n = if (n <= 0) then 1 else n * fact (n - 1)
expr :: forall nt ft. (Integral nt, Fractional ft) => nt -> ft -> ft
expr n x = (x ^ n) / ((fromIntegral (fact n)) :: ft)
Тестирование под ghci
:
$ ghci
GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
λ>
λ> :load q65254539.hs
...
Ok, one module loaded.
λ>
λ> expr 2 3
4.5
λ> expr 2 pi
4.934802200544679
λ>
λ> expr 3 (4::Double)
10.666666666666666
λ>
Это работает! Большое спасибо за объяснение. Я как бы понял это сейчас, но я собираюсь сделать еще немного чтения. Я никогда раньше не использовал fromIntegral
.
Это неявный вызов
show
, сделанныйghci
, который не знает, какой экземплярShow
использовать для преобразования результата вString
. Попробуйтеexpr 3 2 :: Integer
,expr 3 2 :: Float
и т. д., чтобы выбрать один.