2016-10-29 6 views
4

J'ai écrit quelques fonctions utiles pour effectuer une opération logique. Cela ressemble à (a and b or c) `belongs` x. Grâce à Num, IsList et OverloadedLists, je peux l'avoir pour les entiers et les listes.Type de conversion automatique en haskell

λ> (1 && 2 || 3) `belongs` [2] 
False 
λ> (1 && 2 || 3) `belongs` [1, 2] 
True 
λ> (1 && 2 || 3) `belongs` [3] 
True 

λ> :set -XOverloadedLists 
λ> ([1, 2] && [2, 3] || [3, 4]) `contains` 1 
False 
λ> ([1, 2] && [2, 3] || [3, 4]) `contains` 2 
True 

je le fais de cette façon:

newtype BoolLike a = BoolLike ((a -> Bool) -> Bool) 

(&&) :: BoolLike a -> BoolLike a -> BoolLike a 
BoolLike f && BoolLike g = BoolLike $ \k -> f k P.&& g k 
infixr 3 && 

toBoolLike :: a -> BoolLike a 
toBoolLike x = BoolLike $ \k -> k x 

belongs :: Eq a => BoolLike a -> [a] -> Bool 
belongs (BoolLike f) xs = f (\x -> x `elem` xs) 

contains :: Eq a => BoolLike [a] -> a -> Bool 
contains (BoolLike f) x = f (\xs -> x `elem` xs) 

instance Num a => Num (BoolLike a) where 
    fromInteger = toBoolLike . fromInteger 

Ce que je vais faire est de costume pour tous les types, sans convertir manuellement a à BoolLike a.

Comment puis-je l'atteindre?

Premier essai:

class IsBool a where 
    type BoolItem a 
    toBool :: a -> BoolItem a 

instance IsBool (BoolLike a) where 
    type BoolItem (BoolLike a) = BoolLike a 
    toBool = id 

instance IsBool a where 
    type BoolItem a = BoolLike a 
    toBool = toBoolLike 

a échoué:

Conflicting family instance declarations: 
    BoolItem (BoolLike a) -- Defined at BoolLike.hs:54:8 
    BoolItem a -- Defined at BoolLike.hs:58:8 
+0

Il existe le package [Boolean] (https://hackage.haskell.org/package/Boolean). –

Répondre

3

Vous pouvez essayer cette

type family BoolItem a where 
    BoolItem (BoolLike a) = BoolLike a 
    BoolItem a = BoolLike a 

class IsBool a where 
    toBool :: a -> BoolItem a 

instance IsBool (BoolLike a) where 
    toBool = id 

instance (BoolItem a ~ BoolLike a) => IsBool a where 
    toBool = toBoolLike 

En déplaçant la famille de type de la classe, vous pouvez définir pour tous les types . Alors tout ce qui reste restreint l'une des instances. N'oubliez pas que vous devrez également en faire un

+0

Je définis '&&' comme '(toBool -> BoolLike f) && (toBool -> BoolLike g) = BoolLike $ \ k -> f k P. && g k', et' || 'similaire. Ça a l'air bien, jusqu'à 'let relation = 1 && 2 || 3', ghci ne peut pas en déduire (BoolItem a10 ~ BoolLike a3). Le code source complet est https://github.com/qzchenwl/BoolLike/blob/master/src/BoolLike.hs – wenlong

+0

C'est correct pour 'Char'. J'ai mis à jour le ['Main.hs'] (https://github.com/qzchenwl/BoolLike/blob/master/src/Main.hs) – wenlong

+1

@wenlong, c'est parce que' 1', '2', etc. ont un type non monomorphe, c'est-à-dire 'Num a => a'. Si le type est non ambigu, comme '(1 :: Int) && (2 :: Int) || (3 :: Int) ', ça marche. – dkasak

3

Cette réponse ne vous sera probablement d'aucune utilité car, selon toute vraisemblance, vous avez déjà envisagé l'alternative que je vais suggérer et je ne l'ai pas jugée suffisante pour tes buts diaboliques. Pourtant, les lecteurs qui trébuchent sur cette question pourraient trouver utile de savoir comment réaliser quelque chose de semblable à ce que vous cherchiez, sinon tout à fait aussi astucieux, sans tricherie de classe type.

Dans vos plans, un BoolLike a ...

newtype BoolLike a = BoolLike ((a -> Bool) -> Bool) 

... se compose d'une fonction qui produit un résultat Bool quand donné une suite a -> Bool. Vos exemples d'utilisation se résument à combiner les résultats Bool avec (&&) et (||) avant de fournir la suite. Cela peut être réalisé en utilisant l'instance Applicative pour les fonctions (dans ce cas, (->) (a -> Bool)) et en utilisant (&)/flip ($) pour promouvoir les valeurs simples dans (a -> Bool) -> Bool « calculs suspendus »:

GHCi> ((||) <$> ((&&) <$> ($ 1) <*> ($ 2)) <*> ($ 3)) (`elem` [2]) 
False 

que, bien sûr, ne sont pas du tout bien rangé écrire. Cependant, nous pouvons améliorer les choses beaucoup en définissant:

(Pour une petite bibliothèque les définir, un coup d'oeil à control-bool.)

Armé avec ces derniers, le bruit de ligne supplémentaire devient tout à fait léger:

GHCi> (($ 1) <&&> ($ 2) <||> ($ 3)) (`elem` [2]) 
False 

Cela fonctionne hors de la boîte pour le cas contains ainsi - tout ce qu'il faut est en train de changer la suite fourni:

GHCi> (($ [1, 2]) <&&> ($ [2, 3]) <||> ($ [3, 4])) (elem 1) 
False 

Comme note finale, il convient de signaler le cas contains peut être exprimée en termes carrément de intersect et union de Data.List:

GHCi> [1, 2] `intersect` [2, 3] `union` [3, 4] & elem 1 
False 
+1

Notez également que si vous marquez votre" bottom values ​​"comme vous le faites ici avec' $ 'cela fait aussi disparaître le problème de l'OP puisque les opérateurs ne peuvent être implémentés que pour' BoolLike' et n'ont pas besoin d'être polymorphes. –