2017-02-21 11 views
2

Я играл со случайной функцией, которая дает бесконечный список случайных значений, как показано в главе 9 «Узнайте, что вам Haskell для Великого добра». Код выглядит следующим образом:Ошибка Haskell: Не удалось вывести (Random a0), возникающий из

randoms' :: (RandomGen g, Random a) => g -> [a] 
randoms' gen = let (value, newGen) = random gen 
       in value : randoms' newGen 

Для записи случайных генераторов, я изменил randoms' немного к следующему:

randoms'' :: (RandomGen g, Random a) => g -> [(a, g)] 
randoms'' gen = let (value, newGen) = random gen 
       in (value, newGen) : randoms'' newGen 

Он работает, как ожидалось.

Тогда я переделал его в стиле списка понимания:

randoms''' :: (RandomGen g, Random a) => g -> [(a, g)] 
randoms''' gen = random gen : [random gen' | (_, gen') <- randoms''' gen] 

На этот раз, компилятор выдает ошибку: Не удалось вывести (Random a0), вытекающее из использования «Randoms» «»» ...... Тип переменной 'a0' неоднозначен ...

Однако, если указать тип randoms''' с конкретными типами, например,

randoms''' :: StdGen -> [(Int, StdGen)] 
randoms''' gen = random gen : [random gen' | (_, gen') <- randoms''' gen] 

он отлично работает и дает точно такой же результат, как randoms''.

Интересно, почему вывод типа работает для randoms'', но не для randoms'''. Может ли кто-нибудь сказать мне, почему эти два не эквивалентны и как исправить код randoms'''?

Кроме того, я экспериментировал с тест-код, имеющий подобную структуру:

generate :: (Integral a, RealFrac b) => a -> (b,a) 
generate m = let x = 1.2^^m in (x, ceiling x) 

foo :: (Integral g, RealFrac a) => g -> [(a,g)] 
foo gen = let (value, newGen) = generate gen 
      in (value, newGen) : foo newGen 

foo' :: (Integral g, RealFrac a) => g -> [(a, g)] 
foo' gen = generate gen : [generate gen' | (_, gen') <- foo' gen] 

foo'' :: (Integral g, RealFrac a) => g -> [(a, g)] 
foo'' gen = [generate gen' | gen' <- gen : map snd (foo'' gen)] 

Оказывается, foofoo' и foo'' все работают отлично. По-видимому, это не проблема мономорфизма и полиморфизма. Кажется, это проблема, характерная для random.

+1

Вы отбрасываете фактическое значение, генерируемое 'randoms '' '' ('(_, gen') <- ...'), поэтому компилятор не имеет представления о том, какой тип создает экземпляр использования 'randoms ' «'; нет никаких оснований для того, чтобы он выбирал тот же тип 'a', который является выходом, или каким-либо другим конкретным типом, поэтому он неоднозначен. В мономорфном случае невозможно создать экземпляр - естественно, он может выбрать только «Int» для рекурсивного случая. – user2407038

ответ

2
(_, gen') <- randoms''' gen 

Какой тип _? Вероятно, вы думаете, что это должно быть a, указанное в подписи randoms'''. Однако это произвольно. Это может быть (), это может быть Char. Здесь все может работать, так как вы все равно не используете его. Мы можем заставить его иметь тот же тип, random gen':

randoms''' gen = random gen : [random gen' `asTypeOf` x | [email protected](_, gen') <- randoms''' gen] 

asTypeOf является стандартной функцией и в основном типа ограничена версия const:

asTypeOf :: a -> a -> a 
asTypeOf = const 

С помощью этой функции можно определить тип x и, следовательно, тип _, так как известен тип random gen'.

+0

Ваше первое решение с использованием 'asTypeOf' решает проблему.Однако второе решение 'randomList' дает почти точно такое же сообщение об ошибке (и оно работает снова, если сигнатура типа задана как мономорфная). Я до сих пор не понимаю, почему тип '_' произволен просто потому, что он не используется; такой проблемы в мономорфном случае нет. Возможно, механизм вывода типа несколько отличается между мономорфными и полиморфными случаями? – Oyu

+0

@Oyu Я удалил неправильную функцию. Я понятия не имею, почему это сработало, когда я попробовал. В любом случае, '_' используется«. Это часть результата «случайного» вызова. Если мы используем 'let (val, gen) = random (mkStdGen 0) :: (a, StdGen)', мы получим другой генератор 'gen' в зависимости от типа. Для 'a ~ Int' мы получаем' (9106162675347844341,1346387765 2103410263) '. Для 'a ~ Char' мы получаем' ('\ 589059', 40014 40692) '. В конце концов, вы должны называть 'next' достаточно часто, чтобы создать равномерный дистрибутив по всему диапазону значений. – Zeta

+0

Я экспериментировал с некоторыми кодами foo, которые были отредактированы в конце моего исходного сообщения. Можете ли вы дать мне несколько комментариев о том, что вы думаете? – Oyu

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

  • Нет связанных вопросов^_^