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

Minimal complete definition with dependent functions

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

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 编译指示:

{-# MINIMAL initializePure | initialize #-}

但随后我收到了不同的警告:

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

我的意图是通过提供 initializeinitializePure 来获得 Initializable,但仅在 initialize 之外使用定义。

如何干净地编译代码?

编译器对此发出警告是非常正确的,因为它不能可以在只能用 IO 初始化的类型上使用 initialisePure

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

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