Я решаю интересный пример haskell, который нашел в Интернете, поэтому я кастрировал приведенный ниже код, чтобы все еще воспроизводить по типу ошибки, но не раскрывать никаких решений:

import Control.Monad
import Data.List(permutations)

permutationsUpTo :: Int -> [a] -> [[a]]
permutationsUpTo 0 _ = []
permutationsUpTo _ [] = []
permutationsUpTo n (x:xs) = (permutationsUpTo (n) (xs)) ++ permutations (x:xs)

-- function borrowed from rosetta code
nthRoot n x = fst $ until (uncurry(==)) (\(_,x0) -> (x0,((n-1)*x0+x/x0**(n-1))/n)) (x,x/n)

integerNthRoot n = ceiling . (nthRoot n) . fromIntegral

algorithm :: Int -> Int -> [[Int]]
algorithm x n = do
    perm <- permutationsUpTo x [1..(integerNthRoot n x)]
    guard ((sum perm) == x)
    return perm

Когда я пытаюсь скомпилировать этот код, я получаю:

No instance for (RealFrac Int)
  arising from a use of `integerNthRoot'
Possible fix: add an instance declaration for (RealFrac Int)
In the expression: (integerNthRoot n x)
In the second argument of `permutationsUpTo', namely
  `[1 .. (integerNthRoot n x)]'
In a stmt of a 'do' block:
  perm <- permutationsUpTo x [1 .. (integerNthRoot n x)]

Этот ответ был особенно полезен, помогая мне понять, что происходит, но я все еще могу Кажется, эта ошибка не исправлена.

Заранее спасибо!

2
Lev Dubinets 8 Янв 2014 в 01:40

2 ответа

Лучший ответ

Проблема возникла из-за вашего кода из Rosetta Code. Если вы изучили тип nthRoot в GHCi, это было

nthRoot :: (Eq a, Fractional a) => a -> a -> a

Но то, что вы действительно хотели, было

nthRoot :: (Integral a, Eq b, Floating b) => a -> b -> b

Если вы добавите эту подпись типа, ваша ошибка возникнет из арифметики между n, x и x0. Простое исправление:

nthRoot :: (Integral a, Eq b, Floating b) => a -> b -> b
nthRoot n x = fst $ until (uncurry (==)) (\(_, x0) -> (x0, ((n' - 1) * x0 + x / x0 ** (n' - 1)) / n')) (x, x / n')
    where n' = fromIntegral n

Тогда ваша функция integerNthRoot имеет тип

integerNthRoot :: (Integral a, Integral b, Integral c) => a -> b -> c

И algorithm проверки типов.

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


Поскольку у вас есть Eq b, Floating b в nthRoot, может быть лучше просто переключиться на использование Double (предложено hlint). Тогда у тебя просто было бы

nthRoot :: Integral a => a -> Double -> Double
nthRoot = ...

И я заметил, что вы, вероятно, предпочли бы

integerNthRoot :: (Integral a, Integral b) => a -> b -> b
integerNthRoot = ...
2
bheklilr 7 Янв 2014 в 23:57

Сигнатура типа algorithm фиксирует тип n как Int, поэтому вам потребуется nthRoot (fromIntegral n).

(Это действительно, действительно помогает дать сигнатуры типа всему верхнему уровню в Haskell, даже если вы не пытаетесь отладить ошибку типа.)

1
Tom Ellis 7 Янв 2014 в 22:05