Прозрачная реализация определенной формы динамической типизации - PullRequest
7 голосов
/ 02 апреля 2012

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

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


Я не уверен, как правильно решить эту проблему; Я пытался составить «составные» типы, делая что-то вроде:

data CompoundCoeff = CompoundInt Int | CompoundDouble Double | ...

где Int, Double, ... являются экземплярами класса 'Коэффициент'.
Однако это стало большим усилием для адаптации всех функций, задействованных в коде, для работы с этими составными типами (и это на самом деле не очень хорошее решение). Было бы хорошо, если бы все функции имели одинаковый, простой тип, например

Coefficient a => a -> (stuff not involving a anymore)

но, к сожалению, это не так.

Еще одна проблема, с которой я столкнулся, заключается в том, что я использую семейства типов и что-то вроде

class (Monoid (ColourData c), Coordinate (InputData c)) => ColourScheme c where
    type ColourData c :: *
    type InputData c  :: *
    colouriseData     :: c -> (ColourData c) -> AlphaColour Double
    processInput      :: c -> InputData c -> ColourData c

Это не проходит чисто, если мне нужно использовать какой-то составной тип данных ColourData, как и предыдущий; в частности, я больше не могу гарантировать, что поток данных дает непротиворечивый тип (а не просто разные «подтипы» составного типа), и что (помимо прочего) придется создавать поддельный экземпляр Monoid, если я действительно составляю составной тип ColourData.

Я также изучил Data.Dynamic, но, опять же, я не вижу, как он будет правильно решать проблемы; точно такие же проблемы появляются (ну, даже немного хуже, учитывая, что, как я понимаю, существует только один «универсальный» динамический тип).


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

Ответы [ 2 ]

7 голосов
/ 02 апреля 2012

Традиционный способ предоставить объект, который гарантирует, что он является экземпляром класса типов Foo, но не дает никаких дополнительных гарантий, выглядит следующим образом:

 {-# LANGUAGE ExistentialTypes #-}
 data SomeFoo = forall a . Foo a => SomeFoo a

 instance Foo SomeFoo where
   -- all operations just unwrap the SomeFoo straightforwardly

или, с GADT, которые могут бытьболее читабельно ...

 data SomeFoo where
   SomeFoo :: Foo a => a -> SomeFoo
6 голосов
/ 02 апреля 2012

Одним из предложений будет написать одну функцию верхнего уровня, которая выполняет все последние штрихи после того, как вы выбрали тип:

topLevel :: SomeTypeClass a => a -> IO ()

Ваша программа может быть написана примерно так:

main = do
    config <- readConfig
    case config of
        UseDouble n -> topLevel n
        UseSymbolic x -> topLevel x
        UseWidgetFrobnosticator wf -> topLevel wf
...