2016-10-29 3 views
4

Ich habe einige hilfreiche Funktionen geschrieben, um logische Operation zu tun. Es sieht wie (a and b or c) `belongs` x aus.Auto konvertieren Typ in Haskell

Dank Num, IsList und OverloadedLists kann ich es für ganze Zahlen und Listen haben.

λ> (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 

ich es auf diese Weise:

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 

Was ich zu tun ist, es für alle Arten machen passen, ohne manuell a zu BoolLike a konvertieren.

Wie kann ich es erreichen?

Erster Versuch:

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 

Failed:

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

Es gibt das Paket [Boolean] (https://hackage.haskell.org/package/Boolean). –

Antwort

3

Sie diese aus der Klasse

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 

Durch Bewegen der Art Familie könnten versuchen Sie es für alle Typen definieren . Dann bleibt nur eine der Instanzen zu beschränken. Vergiss nicht, dass du das auch machen musst. OVERLAPPABLE

+0

Ich definiere '&&' als '(zuBool -> BoolLike f) && (zuBool -> BoolLike g) = BoolLike $ \ k -> fkP. && g k', und' || 'ähnlich. Es sieht gut aus, bis 'let relation = 1 && 2 || 3', ghci kann nicht ableiten (BoolItem a10 ~ BoolLike a3). Der vollständige Quellcode ist https://github.com/qzchenwl/BoolLike/blob/master/src/BoolLike.hs – wenlong

+0

Es ist OK für 'Char'. Ich habe die ['Main.hs'] (https://github.com/qzchenwl/BoolLike/blob/master/src/Main.hs) – wenlong

+1

@wenlong aktualisiert, weil' 1', '2', usw. haben einen nicht-monomorphen Typ, dh "Num a => a". Wenn der Typ eindeutig ist, wie '(1 :: Int) && (2 :: Int) || (3 :: Int) ', es funktioniert. – dkasak

3

Diese Antwort wird dir wahrscheinlich nicht nützlich sein, da du wahrscheinlich die Alternative, die ich vorschlagen werde, bereits erwogen und für nicht genug erachtet hast deine teuflischen Absichten. Dennoch können Leser, die über diese Frage stolpern, es nützlich finden zu wissen, wie man etwas erreicht, das dem ähnlich ist, wonach man sucht, wenn auch nicht ganz so raffiniert, ohne Klassenklauen.

In Ihren Plänen, ein BoolLike a ...

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

... besteht aus einer Funktion, die ein Bool Ergebnis erzeugt, wenn eine Fortsetzung a -> Bool gegeben. Ihre Verwendungsbeispiele gehen alle auf die Kombination der Bool Ergebnisse mit (&&) und (||) vor der Lieferung der Fortsetzung. Das kann unter Verwendung der Applicative Instanz für Funktionen erzielt wird (in diesem Fall (->) (a -> Bool)) und unter Verwendung von (&)/flip ($) Normalwerte in (a -> Bool) -> Bool „suspended Berechnungen“ zu fördern:

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

dass, ist natürlich überhaupt nicht ordentlich schreiben. Allerdings können wir die Dinge ziemlich viel durch die Definition verbessern:

(Für eine kleine Bibliothek definieren sie, einen Blick auf control-bool haben.

)

Bewaffnet mit diesen, die zusätzliche Leitungsrauschen wird ganz leicht:

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

Dies funktioniert aus der Box für den contains Fall auch - alles, was es braucht, ist die mitgelieferte Fortsetzung Wechsel:

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

Als abschließende Bemerkung, es lohnt sich die contains Fall unter Hinweis darauf, kann ohne weiteres in Bezug auf intersect und union von Data.List ausgedrückt werden:

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

Auch erwähnenswert, dass, wenn Sie Ihre" bottom values ​​"Wie Sie es hier mit' $ 'tun, macht es auch das OP-Problem weg, da die Operatoren dann nur für' BoolLike's implementiert werden können und sie nicht polymorph sein müssen. –