2016-05-29 1 views
2

je la classe de types suivants (méthodes non pertinentes omis):définition minimale avec des fonctions dépendant

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 

Certaines implémentations ont besoin IO de s'initialiser, d'autres pas.

Ce code donne un avertissement:

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

J'ai essayé d'ajouter un MINIMAL pragma comme ceci:

{-# MINIMAL initializePure | initialize #-} 

Mais je reçois un avertissement différent:

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

Mon intention est d'avoir Initializable en fournissant soitinitialize ou initializePure, mais en utilisant uniquement initialize en dehors de la définition.

Comment compiler le code proprement?

+0

Qu'espérez-vous 'initializePure :: bar' être, dans le dernier cas? Il n'y a pas de mise en œuvre pour cela: pas dans l'instance, et pas dans la classe. – chi

+0

@chi Je ne m'attends pas à ce que 'initializePure' soit appelé sauf' initialize'. J'ai modifié la question pour inclure cela. – Koterpillar

Répondre

5

Le compilateur a tout à fait raison d'avertir à ce sujet, puisqu'il ne peut pas être possible d'avoir initialisePure sur un type qui ne peut être initialisé avec IO.

La seule façon de rendre cette sécurité est de séparer les deux cas; la plus simple possible serait d'avoir deux classes:

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 

Vous ne pouvez pas donner une implémentation par défaut pour PureInitialisable, car il vient n'existe pas pour certains types, comme Bar. Mais si vous activez DefaultSignatures, vous pouvez donner une valeur par défaut pour Initialisable qui se déclenche lorsque le type se trouve être également PureInitialisable:

{-# LANGUAGE DefaultSignatures #-} 

class Initialisable a where 
    initialise :: IO a 

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

Pour les types comme Foo, cela vous permet d'écrire

instance PureInitialisable Foo where initialisePure = Foo 
instance Initialisable Foo 

qui est légèrement plus court.

Une autre solution pourrait être de faire la monade dans lequel se produit l'initialisation personnalisable:

{-# 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 
+2

... et vous pouvez utiliser 'DefaultSignatures' pour avoir toujours une implémentation par défaut de' initialise' pour les types qui sont également des instances de 'PureInitialisable'. –

+0

@DanielWagner Cette extension me convient parfaitement, voulez-vous faire une réponse? – Koterpillar

+0

@Koterpillar J'ai édité cette réponse pour le mentionner, à la place. –