具有相关函数的最小完整定义

时间:2016-05-29 12:07:28

标签: haskell ghc

我有以下类型类(省略了不相关的方法):

class Initializable a where
    initialize :: IO a
    initialize = return initializePure

    {# convenience method for implementations, not to be called from outside #}
    initializePure :: a

data Foo = Foo

instance Initializable Foo where
    initializePure = Foo

data Bar = Bar

instance Initializable Bar where
    initialize = initializeBar

有些实现需要IO来初始化自己,有些则不需要。

此代码发出警告:

No explicit implementation for
  ‘initializePure’
In the instance declaration for ‘Initializable Bar’

我试图像这样添加MINIMAL pragma:

{-# MINIMAL initializePure | initialize #-}

但后来我收到了另一个警告:

The MINIMAL pragma does not require:
  ‘initializePure’
but there is no default implementation.
In the class declaration for ‘Initializable’

我的目的是通过提供 {/ 1>} InitializableinitializeinitializePure,但仅在定义之外使用initialize

如何干净地编译代码?

1 个答案:

答案 0 :(得分:5)

编译器警告这一点非常正确,因为不能可以在只能用IO初始化的类型上使用initialisePure

使这种安全的唯一方法是将这两种情况分开;最简单的可能性是两个类:

class Initialisable a where
  initialise :: IO a

class Initialisable a => PureInitialisable a where
  initialisePure :: a

data Foo = Foo

instance Initialisable Foo where
  initialise = return Foo
instance PureInitialisable Foo where
  initialisePure = Foo

data Bar = Bar
initialiseBar :: IO Bar
initialiseBar = undefined

instance Initialisable Bar where
  initialise = initialiseBar

您无法为PureInitialisable提供默认实施,因为某些类型的不存在,例如Bar。但是,如果您启用DefaultSignatures,则可以为Initialisable提供默认值,以便在类型恰好为PureInitialisable时启动:

{-# LANGUAGE DefaultSignatures #-}

class Initialisable a where
  initialise :: IO a

  default initialise :: PureInitialisable a => IO a
  initialise = return initialisePure

对于像Foo这样的类型,这可以让你写

instance PureInitialisable Foo where initialisePure = Foo
instance Initialisable Foo

略短。

另一种方法可能是使初始化可以自定义的monad:

{-# LANGUAGE TypeFamilies #-}

class Initialisable a where
  type InitialisationM a :: *
  type InitialisationM a = a
  initialise :: InitialisationM a

instance Initialisable Foo where
  initialise = Foo

instance Initialisable Bar where
  type InitialisationM Bar = IO Bar
  initialise = initialiseBar