Чому цей код Haskell видає помилку "нескінченний тип"?


82

Я новачок у Haskell і стикаюся з помилкою "неможливо побудувати нескінченний тип", яку я не можу зрозуміти.

Насправді, крім цього, я не зміг знайти хорошого пояснення того, що ця помилка навіть означає, тому, якщо ви могли б вийти за рамки мого основного запитання та пояснити помилку "нескінченний тип", я б це дуже вдячний.

Ось код:

intersperse :: a -> [[a]] -> [a]

-- intersperse '*' ["foo","bar","baz","quux"] 
--  should produce the following:
--  "foo*bar*baz*quux"

-- intersperse -99 [ [1,2,3],[4,5,6],[7,8,9]]
--  should produce the following:
--  [1,2,3,-99,4,5,6,-99,7,8,9]

intersperse _ [] = []
intersperse _ [x] = x
intersperse s (x:y:xs) = x:s:y:intersperse s xs

І ось помилка при спробі завантажити її в інтерпретатор:

Prelude> :load ./chapter.3.ending.real.world.haskell.exercises.hs
[1 of 1] Compiling Main (chapter.3.ending.real.world.haskell.exercises.hs, interpreted )

chapter.3.ending.real.world.haskell.exercises.hs:147:0:
Occurs check: cannot construct the infinite type: a = [a]
When generalising the type(s) for `intersperse'
Failed, modules loaded: none.

Дякую.

-

Ось деякі виправлені коди та загальні рекомендації щодо усунення помилки "нескінченного типу" в Haskell:

Виправлений код

intersperse _ [] = []
intersperse _ [x] = x
intersperse s (x:xs) =  x ++ s:intersperse s xs 

У чому була проблема:

Мій підпис типу вказує, що другим параметром для вкраплення є список списків . Тому, коли я збігався з "s (x: y: xs)", x та y ставали списками . І все-таки я поводився з x і y як з елементами, а не як списки.

Вказівки щодо усунення помилки "нескінченного типу":

Здебільшого, коли ви отримуєте цю помилку, ви забуваєте типи різних змінних, з якими ви стикаєтесь, і намагаєтеся використовувати змінну так, ніби це інший тип, ніж той, що вона є. Уважно подивіться, якого типу все в порівнянні з тим, як ви його використовуєте, і це, як правило, виявить проблему.


2
Ще одна хороша порада: явно оголосіть типи. Це дає компілятору щось для перевірки.
Пол Джонсон,

3
Отже, це вирішує проблему, але чому компілятор каже "Не вдається побудувати нескінченний тип?". Що це означає? Якщо проблема полягає в тому, що ви намагаєтесь виконувати операції з типами, які не підтримують ці операції, чому компілятор не каже щось подібне?
freedrull

11
+1 для структури запитання (питання - виправлене - проблема була - орієнтир)
Dacav

2
@freedrull: помилка сформульована таким чином, тому що я сказав компілятору, що параметр типу "a" слід "заповнити" "списком a". Це все одно, що сказати, що моя кавова чашка містить купу копій моєї кавової чашки. Це справді рекурсивне визначення, яке не має сенсу (або, принаймні, визначене як недійсне в Haskell). Отже, це говорить мені: "Неможливо побудувати нескінченний тип". Справа в тому, що я міг би робити інші речі з цим параметром типу, які базуються на. Наприклад, я міг би заповнити цей параметр типу "Можливо". Отже, те, що я робив, - це нормально, але те, що я робив, - це недобре.
Charlie Flowers

1
@freedrull: Компілятор помітив, що код робить купу речей зі значеннями, пов'язаними з ними a, будь-яке з яких було б чудово окремо. Тому він не може вказати на жодного з них і сказати "це непідтримувана операція для цього типу". Але разом вони складають вимогу щодо типу, що задовольняє a = [a]. Компілятор знає, що це неможливо, тому він вам це повідомляє. Він не знає, які частини коду, що призвели до цієї вимоги, є неправильними частинами; це залежить від того, що ви передбачали означати код.
Бен

Відповіді:


36

Проблема полягає в останньому реченні, де ви розглядаєте x та y як елементи, тоді як вони є списками. Це буде працювати:

intersperse _ [] = []
intersperse _ [x] = x 
intersperse s (x:y:xs) = x ++ [s] ++ y ++ intersperse s xs

Помилка нескінченного типу виникає через те, що оператор: має тип a -> [a] -> [a], тоді як ви розглядаєте це як [a] -> a -> [a], що означає, що [a] потрібно ідентифікувати з a, що означало б, що a - це нескінченно вкладений список. Це заборонено (і зовсім не те, що ви маєте на увазі).

Редагувати: у наведеному вище коді також є ще одна помилка. Вона повинна бути:

intersperse _ [] = []
intersperse _ [x] = x
intersperse s (x:xs) = x ++ [s] ++ intersperse s xs

1
Дякую. Я зрозумів, що вони обидва, а потім повернувся сюди і побачив вашу відповідь, що було для мене чудовою перевіркою. Ви також виправили мою помилку краще, ніж я. Моя помилка полягала в тому, що він пропускав розділювач між y та xs. Щоб це виправити, я представив ще один рівень узгодження шаблонів, наприклад: intersperse s (x: y: []) = x ++ s: y intersperse s (x: y: xs) = intersperse s [x, y] ++ s: intersperse s xs Але схоже, ви виправили мою помилку без необхідності в цьому додатковому рівні.
Charlie Flowers

2
Ось урок, який я засвоюю: "Зіткнувшись з помилкою" нескінченного типу ", ви, мабуть, забуваєте, з якими типами маєте справу, а отже, робите те, чого не збиралися робити. Уважно подивіться, якого типу кожна з ваших змінних є, і це зазвичай виявляє проблему ". Чи є щось, що ви могли б додати чи змінити в цьому?
Charlie Flowers

Це, безумовно, правильно, і я б нічого не змінив у цьому. Нескінченні типи заборонені, а отже, нескінченна помилка типу означає, що десь функція отримує аргумент з неправильним типом. Удачі вам у RWH :)
Stephan202

where you treat x and y as elements, while they are lists.Чому вони складають списки, ви цього не пояснили? В x:xs, xце елемент, правда? Я сподівався, що так само буде і в нас x:y:xs. Крім того, якщо вони є списками, як вони діляться, містять скільки елементів? Я здогадуюсь одна?
Nawaz

6

Часто додавання явного визначення типу може зробити так, щоб повідомлення про помилку типу компілятора мало більше сенсу. Але в цьому випадку явне введення погіршує повідомлення про помилку компілятора.

Подивіться, що відбувається, коли я дозволяю ghc вгадувати тип проміжку:

Occurs check: cannot construct the infinite type: a = [a]
  Expected type: [a] -> [[a]] -> [[a]]
  Inferred type: [a] -> [[a]] -> [a]
In the second argument of `(:)', namely `intersperse s xs'
In the second argument of `(:)', namely `y : intersperse s xs'

Це чітко вказує на помилку в коді. Використовуючи цю техніку, вам не потрібно вдивлятися у все і добре думати про типи, як пропонували робити інші.


3

Можливо, я помиляюся, але, схоже, ви намагаєтесь вирішити більш складну проблему. Ваша версія intersperseне просто передає значення масиву, а й вирівнює його на один рівень.

ListМодуль в Haskell фактично забезпечує пересипати функцію. Він позначає значення, вказане між кожним елементом у списку. Наприклад:

intersperse 11 [1, 3, 5, 7, 9] = [1, 11, 3, 11, 5, 11, 7, 11, 9]
intersperse "*" ["foo","bar","baz","quux"] = ["foo", "*", "bar", "*", "baz", "*", "quux"]

Я припускаю, що це те, що ти хочеш зробити, бо це те, що мій професор хотів, щоб ми зробили, коли я навчався Haskell. Я, звичайно, міг би бути повністю.


1
Дякую за коментар. Однак у цьому випадку я хочу вирівняти його на один рівень, тому що виконую вправу 7 з кінця глави 3 "Реального світу Хаскелл".
Charlie Flowers

Маю Якби у мене була книга, я б перевірив, перш ніж писати. На жаль, я міг лише здогадуватися. Радий, що все одно це відсортували. :-)
Самір Талвар

5
Зміст книги робиться у вільному доступі в Інтернеті: book.realworldhaskell.org
Stephan202

Відмінно. Додано в закладки. Дякую за посилання - я буду допомагати в лабораторіях Haskell, які з’являться наступного року, і це, без сумніву, стане в нагоді при чищенні.
Самір Талвар

0

Також я знайшов це, що пояснює значення помилки.

Кожного разу, коли інтерпретатор / компілятор видає мені цю помилку, це тому, що я використовую якийсь параметризований за типом кортеж як формальний параметр. Все працює правильно, видаливши визначення типу функції, яка містила змінні типу.

Я все ще не можу зрозуміти, як це виправити та зберегти визначення типу функції.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.