2013-02-14 4 views
2

У меня есть функция evalExpression :: Exp -> Value в модуле A, который сильно зависит от соответствия шаблону по аргументу Exp.Определение функции в двух разных модулях или обходной путь

Файл стал достаточно большим, чтобы желать больше организации. Я хотел бы разделить модуль A на модули A.GHC.Num, A.GHC.Types и т.д.

  • Есть ли способ, чтобы встроить модуль внутри другого в GHC? Я попытался сделать это, но я получаю сообщение об ошибке «импорт модуля образует цикл»

или

  • Есть ли способ, чтобы написать один и тот же модуль в двух разных файлах?

или

  • Могу ли я определить функцию A.evalExp, которая пытается (в том смысле, попытаться поймать), чтобы вернуть значение A.GHC.Num.evalExp и если он кэширует ошибку (не исчерпывающий поиска по шаблону), пытается вернуть A.GHC.Types.evalExp и т. д.?

Update:

Я попытался решить циклическую зависимость, но GHC не был убежден, что говорит "Неоднозначное появление".

+0

Я добавил тег haskell, но я не уверен, что вопрос относится к нему, поскольку вопрос касается GHC. При необходимости удалите. –

+1

Очевидный вопрос в отношении первого пункта: вы уверены, что не можете избежать циклического импорта? Можете ли вы как-то разделить 'A' таким образом, чтобы устранить необходимость циклического импорта? – gspr

+0

Для большинства практических применений Haskell в целом может быть специфичным для GHC. Не беспокойтесь об этом. Это далекий и самый распространенный компилятор, и большинство людей говорят, что «Haskell» они действительно означают «интерпретацию отчета GHC и некоторые неуказанные расширения GHC». –

ответ

5

Нет, вы не можете разделить модуль между несколькими файлами, и вы определенно не можете определить функцию в разных местах. Самое близкое к этому - это функция, которая является частью класса типа, с экземплярами, определенными в разных модулях. Но это, вероятно, не то, что вы хотите здесь.

Однако является возможно скомпилировать взаимно-рекурсивные модули. Теоретически это должно просто работать (tm), но GHC требует немного обручей, чтобы сделать это; см. the User's Guide для деталей. Если вы получаете ошибку импорта циклического модуля, это должно позволить вам заставить эту версию работать.

Нет никакого «приятного» способа поймать ошибку неисчерпаемого соответствия шаблону и попробовать что-то еще. Есть несколько не очень приятных способов, но вы, вероятно, не хотите туда идти.

Если ваша цель - сопоставление образцов по одному типу данных с большим количеством случаев, самый простой способ разделить вещи, не вступая во взаимодействие с взаимно рекурсивными модулями или типами классов, - это иметь отдельные функции в других модулях, которые принимают содержимое каждого конструктора в качестве прямых аргументов, затем одно совпадение шаблонов во всех случаях в модулях, которые импортируют другие и отправляют.

Скажем, у вас есть тип Foo с A случаев, B, & гр., С аналогичными именами модулей. В «центральном» модуле вы можете:

doStuff (A x y) = A.doStuffA x y 
doStuff (B z) = B.doStuffB z 

... и так далее.

В некоторых сценариях даже имеет смысл разделить весь тип данных аналогичным образом и создать отдельный тип для каждого конструктора, например: data Foo = A FooA | B FooB | .... Это наиболее полезно, когда у вас сложный путаница типов данных, которые могут быть взаимно рекурсивными несколькими способами, классическим примером является AST.

Хорошо, вот один из способов имитировать то, что вы хотите, не делая ничего слишком отрывочного.

Сначала разделите свою функцию на разные модули так, как вам хотелось бы. Затем сделайте следующие изменения:

  • Изменить тип результата использовать Maybe, оберточная результаты в Just и добавляя кетчуп все дела по умолчанию, который производит Nothing.

  • Добавить дополнительный аргумент r, и заменить рекурсивные вызовы на evalExp с r.

Из центрального модуля импортируйте каждый модуль, содержащий evalExp чехлы. При необходимости используйте квалифицированный импорт, чтобы избежать двусмысленности. Определить список каждой Eval функции (все они должны иметь одинаковый тип), а затем определить «реальный» evalExp как что-то вроде этого:

expCases = [A.GHC.Num.evalExp, A.GHC.Types.evalExpr {- etc... -} ] 

evalExpCases exp = mapMaybe (\eval -> eval evalExp exp) expCases 

evalExp exp = case evalExpCases exp of 
        (r:_) -> -- use the first result 
        [] -> -- no cases matched 

По существу, это с помощью Maybe указать неисчерпаемые модели в явном виде, и заменяющее прямую рекурсию с конструкцией стиля fix, в которой объединенная, рекурсивная функция передается каждому (индивидуально нерекурсивному) множеству случаев.

Это довольно неудобно, но я не уверен, что есть лучший способ. Возможно, есть способ автоматизировать все, что дерьмо, используя Template Haskell, но это, вероятно, будет так же сложно, как делать это вручную.

Лично я бы, наверное, просто потрогал зубы и оставил все в одном модуле.

+0

Спасибо за ответ. Не могли бы вы рассказать о решении типа класса? Я сопоставляю над 'Language.Core.Core.Exp' из пакета' extcore'. и у меня есть соответствующие шаблону выражения, такие как 'evalExp (Var ((Just (M (P (« base »), [« GHC »],« Num »))),« zp »)) = ..' для каждой функции bultin в базе и ghc-prim. Таким образом, проблема, на мой взгляд, соответствует определенным строкам. Я хочу, чтобы я мог сопоставлять конструктор типа Var в нескольких файлах. EDIT: Я думаю, что это может быть достигнуто связыванием переменных со строками, затем сопоставление пакета с регулярными выражениями, а затем вызов надлежащего eval –

+0

@ CarlosLópez-Camey: типы типов, вероятно, не помогут вам здесь, так как вы имеют существующий тип данных и очень однородные случаи. Является ли 'evalExp' рекурсивным? –

+0

@CAMcCann. Я вижу .. Да, 'evalExp' рекурсивный. –

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