Методики відстеження обмежень


322

Ось сценарій: я написав код з підписом типу, і GHC скарги не змогли вивести x ~ y для деяких xі y. Зазвичай можна закинути GHC кісткою і просто додати ізоморфізм до обмежень функції, але це погана ідея з кількох причин:

  1. Це не підкреслює розуміння коду.
  2. Ви можете закінчити 5 обмежень, де одного було б достатньо (наприклад, якщо 5 мають на увазі ще одне певне обмеження)
  3. Ви можете вирішити помилкові обмеження, якщо ви зробили щось не так або якщо GHC не допомагає

Я щойно провів кілька годин, бореться з випадком 3. Я граю syntactic-2.0, і я намагався визначити незалежну від домену версію share, схожу на версію, визначену в NanoFeldspar.hs.

У мене було таке:

{-# LANGUAGE GADTs, FlexibleContexts, TypeOperators #-}
import Data.Syntactic

-- Based on NanoFeldspar.hs
data Let a where
    Let :: Let (a :-> (a -> b) :-> Full b)

share :: (Let :<: sup,
          Domain a ~ sup,
          Domain b ~ sup,
          SyntacticN (a -> (a -> b) -> b) fi) 
      => a -> (a -> b) -> a
share = sugarSym Let

і GHC could not deduce (Internal a) ~ (Internal b), що, звичайно, не те, за що я йшов. Отже, або я написав якийсь код, який я не мав наміру (який вимагав обмеження), або GHC хотів, щоб це обмеження було обумовлене деякими іншими обмеженнями.

Виявляється, мені потрібно було додати (Syntactic a, Syntactic b, Syntactic (a->b))список обмежень, жоден з яких не означає (Internal a) ~ (Internal b). Я в основному натрапив на правильні обмеження; У мене ще немає систематичного способу їх пошуку.

Мої запитання:

  1. Чому GHC запропонував це обмеження? Ніде в синтаксиці немає обмежень Internal a ~ Internal b, тож звідки GHC це взяв?
  2. Загалом, які методи можна використовувати для відстеження походження обмеження, яке, як вважає GHC, потребує? Навіть щодо обмежень, які я можу виявити сам, мій підхід, по суті, є грубим змушенням порушника шляхом фізичного записування рекурсивних обмежень. Такий підхід в основному знижує нескінченну кролячу діру обмежень і є приблизно найменш ефективним методом, який я можу собі уявити.

21
Існували певні дискусії щодо налагоджувача на рівні типу, але загальний консенсус, схоже, показує внутрішню логіку шейкера, що не допоможе: / На сьогоднішній день вирішувач обмежень Хаскелла - це хитра непроста мова логіки :)
Даніель Гратцер

12
@jozefg У вас є посилання на це обговорення?
crockeea

36
Часто це допомагає повністю видалити підпис типу і дозволити ghci розповісти, що він вважає за підпис.
Тобіас Брандт

12
Якось aі bпов'язане - подивіться на підпис типу поза вашим контекстом - a -> (a -> b) -> a, ні a -> (a -> b) -> b. Може, це все? За допомогою вирішувачів обмежень вони можуть впливати на перехідну рівність де завгодно , однак помилки зазвичай показують місце "близько" до місця, де викликано обмеження. Це було б круто, але @jozefg - можливо, коментуючи обмеження тегами чи щось таке, щоб показати, звідки вони взялися? : s
Атан Кларк

Відповіді:


6

Перш за все, ваша функція має неправильний тип; Я впевнений, що це має бути (без контексту) a -> (a -> b) -> b. GHC 7.10 дещо корисніше, щоб вказати на це, оскільки з оригінальним кодом він скаржиться на відсутність обмежень Internal (a -> b) ~ (Internal a -> Internal a). Після виправлення shareтипу, GHC 7.10 залишається корисним у керуванні нами:

  1. Could not deduce (Internal (a -> b) ~ (Internal a -> Internal b))

  2. Додавши вище сказане, отримуємо Could not deduce (sup ~ Domain (a -> b))

  3. Після додавання цього ми отримуємо Could not deduce (Syntactic a), Could not deduce (Syntactic b)іCould not deduce (Syntactic (a -> b))

  4. Після додавання цих трьох він остаточно вводить перевірку; тому ми закінчуємо

    share :: (Let :<: sup,
              Domain a ~ sup,
              Domain b ~ sup,
              Domain (a -> b) ~ sup,
              Internal (a -> b) ~ (Internal a -> Internal b),
              Syntactic a, Syntactic b, Syntactic (a -> b),
              SyntacticN (a -> (a -> b) -> b) fi)
          => a -> (a -> b) -> b
    share = sugarSym Let
    

Тож я б сказав, що GHC не був марним у тому, щоб нас вести.

Що стосується вашого запитання щодо відстеження, звідки GHC отримує свої обмежувальні вимоги, ви можете спробувати , зокрема, прапори налагодження GHC-ddump-tc-trace , а потім прочитати журнал, що виходить, щоб побачити, де Internal (a -> b) ~ tі (Internal a -> Internal a) ~ tдодані до Wantedнабору, але це буде досить довго читати .


0

Ви пробували це в GHC 8.8+?

share :: (Let :<: sup,
          Domain a ~ sup,
          Domain b ~ sup,
          SyntacticN (a -> (a -> b) -> b) fi,
          _) 
      => a -> (a -> b) -> a
share = sugarSym Let

Ключовим є використання отвору типу серед обмежень: _ => your difficult type

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