2016-03-22 5 views
4

Betrachten Sie den Code unten:Liste der polymorphen Funktionen in Haskell?

t1 :: [Int] -> (Int,String) 
t1 xs = (sum xs,show $ length xs) 

t2 :: [Int] -> (Int,String) 
t2 xs = (length xs, (\x -> '?') <$> xs) 

t3 :: [Int] -> (Char,String) 
t3 (x:xs) = ('Y',"1+" ++ (show $ length xs)) 
t3 []  = ('N',"empty") 

Diese drei Funktionen haben einen Typ, der nur teilweise variiert - sie vollständig nutzbar sind, ohne die Art der ersten Komponente des Tupels wissen zu müssen sie produzieren. Das bedeutet, dass ich auf sie arbeiten können, ohne auf diese Art zu beziehen zu benötigen:

fnListToStrs vs fs = (\x -> snd $ x vs) <$> fs 

Laden diese Definitionen in GHCi, alle drei der Funktionen fnListToStrs unabhängig als Argument arbeiten, und in der Tat kann ich in einer Liste übergeben beide T1 und t2 enthalten, da sie die gleiche Art haben:

*Imprec> fnListToStrs [1,2] [t1,t2] 
["2","??"] 
*Imprec> fnListToStrs [1,2] [t3] 
["1+1"] 

Aber ich nicht alle drei zur gleichen Zeit passieren kann, auch wenn die Divergenz der Arten der Berechnung tatsächlich irrelevant ist ausgeführt:

*Imprec> fnListToStrs [1,2] [t1,t2] 
["2","??"] 
*Imprec> fnListToStrs [1,2] [t3] 
["1+1"] 

Ich habe das Gefühl, dass diese Arbeit etwas mit entweder existentiellen oder imprädikative Typen zu tun hat, zu machen, aber weder Erweiterung hat für mich gearbeitet, wenn die Typdeklaration mit erwarte ich fnListToStrs, nämlich zu ergreifen, um die Lage sein:

fnListToStrs :: [Int] -> [forall a.[Int]->(a,String)] -> [String] 

Gibt es einen anderen Weg, um dies zu funktionieren?

Antwort

3

Existential ist korrekt, nicht impredicative. Und Haskell hat keine Existenzials, außer durch einen expliziten Wrapper ...

{-# LANGUAGE GADTs #-} 

data SomeFstRes x z where 
    SFR :: (x -> (y,z)) -> SomeFstRes x z 

> fmap (\(SFR f) -> snd $ f [1,2]) [SFR t1, SFR t2, SFR t3] 
["2","??","1+1"] 

aber das ist wirklich ein bisschen nutzlos. Da Sie nicht vielleicht etwas mit dem ersten Ergebnis ohnehin tun können, ist es sinnvoller, um es einfach sofort wegzuwerfen und setzen die verbleibende Funktion in einem einfachen monomorphic Liste:

> fmap ($[1,2]) [snd . t1, snd . t2, snd . t3] 
["2","??","1+1"] 
+0

Es ist möglich, dass ich stark vereinfacht Dinge haben ein bisschen in meine Frage, die mich unsicher macht, wie man das tatsächlich anwendet.In meinem tatsächlichen Szenario sind die Funktionen bereits in einen Typ eingeschlossen, aber beide Ergebnistypen werden als Parameter für den Typ verfügbar gemacht. Ich muss effektiv eine Liste erstellen, die einen der Parameter ignoriert, und während ich versuche, eine vereinfachte API zu erstellen, würde ich es eher bevorzugen, dass Clients keinen expliziten Wrapper selbst verwenden müssen. Aber es sieht so aus, als ob das nicht möglich ist ... – Jules

3

Jeder Weg, um diese Funktionen in eine Liste zu setzen, muss jedes von ihnen in irgendeiner Weise "wickeln". Die einfachste Verpackung ist nur

wrap :: (a -> (b, c)) -> a -> c 
wrap f = snd . f 

Es gibt in der Tat andere Möglichkeiten, diese (vor allem mit existenziellen Typen) zu wickeln, aber Sie haben keine Informationen gegeben zu legen nahe, dass irgendwelche von denen sogar etwas besser wären, in Ihrem Anwendung als diese einfachste Version.

Hier ist ein Beispiel, wo etwas raffinierter Sinn machen könnte. Angenommen, Sie

data Blob a b = Blob [a -> b] [a] 

haben Nun stellen Sie eine Liste von Werten vom Typ Blob a b machen wollen, die alle denselben b Art haben, können aber unterschiedliche a Typen haben. Eigentlich jedes Argument jede Funktion anwenden könnte zu einer untragbar groß Liste der möglichen Ergebnissen führen, so wäre es sinn

data WrapBlob b where 
    WrapBlob :: Blob a b -> WrapBlob b 

Jetzt können Sie die Liste machen schreiben machen und verschieben die Entscheidung, welche Funktion (en) anzuwenden zu welchen Argumenten, ohne einen prohibitiven Preis zu zahlen.