2017-09-09 6 views
2

J'écris une classe Separate qui effectue certaines opérations indépendamment sur des "parties" de différentes structures; par exemple. les parties réelles et complexes d'un nombre complexe (plus tard, j'en aurai aussi besoin pour les listes). De cette façon, je peux écrire des fonctions qui ne se soucient pas de la structure sur laquelle elles agissent mais qui le font de manière indépendante.Instance de complexe double

Les fonctions doivent fonctionner normalement sur des structures sans pièces, par ex. Double. Ceci sera utilisé entre autres pour calculer les écarts-types (je sais qu'il existe un écart-type défini pour les nombres complexes qui donne un nombre réel mais que le nombre "indépendant" a plus de sens dans mon cas).

Mais j'ai des problèmes avec certaines déclarations d'instance. Dans un particulier des opérations devrait être « multiplier par un Double », alias scale:

class Separate a where 
    scale :: Double -> a -> a 

instance Separate Double where 
    scale = (*) 

instance (Floating a) => Separate (Complex a) where 
    d `scale` z = (*d) <$> z 

Bien sûr, ne compile pas parce que * n'est pas défini entre un Double et un Floating a général. Mais je ne peux pas définir directement instance Separate (Complex Double) where....

Je pourrais simplement écrire une fonction Double -> Complex Double -> Complex Double sans classe, mais l'écart-type doit être défini séparément pour différentes structures.

Des idées?

+2

utiliser juste FlexibleInstances. –

Répondre

4

Comme n.m. souligne, définissant instance Separate (Complex Double) est parfaitement bien si vous utilisez l'extension FlexibleInstances, qui est une extension populaire et inoffensive.

Mais il y a d'autres options aussi, par exemple:

instance (Floating a) => Separate (Complex a) where 
    d `scale` z = (* realToFrac d) <$> z 

Et l'instance de composition (the best kind of instance):

instance (Separate a) => Separate (Complex a) where 
    d `scale` z = (d `scale`) <$> z 
+0

Nice! J'aime la dernière option. Mais pourquoi le compilateur ne se plaint-il pas de cela? il ne peut pas savoir que l'échelle est définie par un a donné (par exemple Int)? Est-ce que cela se complique si vous l'utilisez plus tard dans un contexte où cela peut arriver? Je devrais probablement lire sur les instances de composition. – jorgen

+1

@jorgen Notez la clause '(Separate a) =>', qui garantit que 'scale' est défini pour' a'. Cette instance dit 'Complex' * préserve *' Separate'ness. – luqui

+0

Ah, a du sens – jorgen

2

Ce problème est le type plutôt que d'un problème avec la façon dont vous gérez les instances. Comme vous le dites, (*) n'est pas défini entre un Double et un Float. Cependant, nous pouvons facilement convertir entre un Double et un Float, donc vous avez de la chance! Avec realToFrac, nous pouvons convertir entre différents types fractionnaires.

Je réécrivaient l'instance comme suit:

instance (Floating a) -> Separate (Complex a) where 
    d `scale` z = (* (realToFrac d)) <$> z