透明地实现特定形式的动态类型

Sam*_*ire 7 haskell types dynamic

基本思想是我有一系列函数可以处理来自特定类的任何类型,但在运行时,程序应该读取配置文件并提取类中其中一种类型的元素.

例如,我有一个'Coefficient'类,它的各种实例,以及与该类类型多态的各种类型的函数; 在运行时,将确定并传递该类的一种特定类型.


我不确定如何妥善解决这个问题; 我尝试制作'复合'类型,做类似的事情:

data CompoundCoeff = CompoundInt Int | CompoundDouble Double | ...
Run Code Online (Sandbox Code Playgroud)

其中Int,Double,...是"Coefficient"类的实例.
然而,它开始成为一项重大努力,以适应代码中涉及的所有函数来使用这些复合类型(并且它也不是一个很好的解决方案,真的).如果所有函数都具有相同的简单类型,那就没关系了

Coefficient a => a -> (stuff not involving a anymore)
Run Code Online (Sandbox Code Playgroud)

但遗憾的是并非如此.

我遇到的另一个问题是,我正在使用类型系列,并且有类似的东西

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
Run Code Online (Sandbox Code Playgroud)

如果我必须使用某种复合ColourData数据类型(如前一个),那么这不会干净利落; 特别是我不能再保证数据流给出一致的类型(而不仅仅是复合类型的不同'子类型),并且如果我组成了一个,那么(除其他外)必须构成一个虚假的Monoid实例复合ColourData类型.

我也研究过Data.Dynamic,但我再也看不出它如何正确解决问题; 似乎出现了完全相同的问题(好吧,甚至稍差,因为我理解只有一个'通用'动态类型).


问题:如何实现从属于特定类的动态数据类型,而不必重写涉及这些数据类型的所有函数?如果我不必牺牲任何类型的安全性,那将是最好的,但我不是太乐观.
程序应该在运行时读取配置文件,并且应用相关类的所有必需函数(多态).

Lou*_*man 7

提供一个保证它是类型类的实例Foo但不做任何额外保证的对象的传统方法是这样的:

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

 instance Foo SomeFoo where
   -- all operations just unwrap the SomeFoo straightforwardly
Run Code Online (Sandbox Code Playgroud)

或者,与GADT一起,可能更具可读性......

 data SomeFoo where
   SomeFoo :: Foo a => a -> SomeFoo
Run Code Online (Sandbox Code Playgroud)


Dan*_*ner 6

一个建议就是编写一个顶级函数,在您选择一个类型后执行所有最后润色:

topLevel :: SomeTypeClass a => a -> IO ()
Run Code Online (Sandbox Code Playgroud)

然后您的程序可以这样编写:

main = do
    config <- readConfig
    case config of
        UseDouble n -> topLevel n
        UseSymbolic x -> topLevel x
        UseWidgetFrobnosticator wf -> topLevel wf
Run Code Online (Sandbox Code Playgroud)