2013-04-06 7 views
5

ich auf einer hList Implementierung bin zu arbeiten und ich bin fest versuchen, eine map Funktion für sie umzusetzen. Ich habe viele verschiedene Ansätze ausprobiert, aber mit jedem erreiche ich Compiler Fehler in Bezug auf diese Funktion.Mapping über ein heterogenes Datenstruktur mit einer generischen Funktion

Es folgt ein Beispiel dafür, wie ich will Just eine generische Funktion verwenden, um alle Elemente der Eingabedatenstruktur anzuwenden.

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleInstances #-} 

-- | An input heterogenous data structure 
recursivePairs :: (Int, (Char, (Bool,()))) 
recursivePairs = (1, ('a', (True,()))) 

-- | This is how I want to use it 
recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool,()))) 
recursivePairs' = hMap Just recursivePairs 

class HMap f input output where 
    hMap :: f -> input -> output 

-- | A counterpart of a Nil pattern match for a list 
instance HMap f()() where 
    hMap _ _ =() 

-- | A counterpart of a Cons pattern match for a list 
instance 
    (HMap f iTail oTail, 
    Apply f iHead oHead) => 
    HMap f (iHead, iTail) (oHead, oTail) 
    where 
    hMap f (head, tail) = (apply f head, hMap f tail) 

class Apply f input output where 
    apply :: f -> input -> output 

instance Apply (input -> output) input output where 
    apply = id 

Damit erhalte ich den folgenden Compiler-Fehler:

No instance for (Apply (a0 -> Maybe a0) Int (Maybe Int)) 
    arising from a use of `hMap' 
The type variable `a0' is ambiguous 

Gibt es überhaupt eine Möglichkeit, dies zu lösen, und wenn nicht, dann warum?

+0

ich glaube, das Problem ist, dass das Typsystem nicht erkennen, dass Sie bei jeder aufeinanderfolgenden Anwendung, weil Ihre Definition von '' hMap' Just' mit verschiedenen Betontypen instanziieren den gleichen 'f' hält Wiederverwendung. Das erste Mal, wenn Sie es die Art anzuwenden ist 'Int -> Vielleicht Int', das zweite Mal, wenn Sie es gelten die Art ist' Char -> Vielleicht Char'. Ich bin mir aber immer noch nicht ganz sicher, wie ich das beheben soll. –

+0

@GabrielGonzalez Ja, das ist genau das Problem. Und wenn Sie ein fundep '| hinzufügen Input-Output -> f' zum 'Apply' Klasse, werden die Fehlermeldungen sagen, dass es für Instanzen sucht, wie' (Bool -> Vielleicht Bool) Char (Vielleicht Char) '. Ich habe darüber nachgedacht, ['cast'] zu benutzen (http://hackage.haskell.org).org/packages/archive/base/neuste/doc/html/Data-Typeable.html # v: cast), um die beiden Verwendungen von 'f' auf Typ-Ebene zu trennen, aber das fühlte sich einfach nicht sehr natürlich an abhängig von 'Typable' war auch nicht sehr verlockend. –

Antwort

5

Das Problem ist, dass Sie versuchen, eine polymorphe Funktion mit verschiedenen Argumenten zu verwenden, aber Ihre Apply Instanz nimmt eine Funktion (ein Monotyp). Sie können dies mehrere Möglichkeiten leicht beheben

data JustIfy = JustIfy 
instance Apply JustIfy a (Maybe a) where 
    apply _ = Just 

recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool,()))) 
recursivePairs' = hMap JustIfy recursivePairs 

Arbeiten mit Ihrem Code einfach gut

EDIT: Ein allgemeiner Ansatz für die gleiche Sache (erfordert RankNTypes)

--A "universal" action that works on all types 
newtype Univ f = Univ (forall x. x -> f x) 
instance Apply (Univ f) x (f x) where 
    apply (Univ f) x = f x 

recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool,()))) 
recursivePairs' = hMap (Univ Just) recursivePairs 

oder wenn Sie Verwenden einer aktuellen ish-Version von GHC und sind bereit, weitere Erweiterungen zu aktivieren

das ist nett seit dann können Sie Dinge tun, wie eine "Show" in die Funktion, die Sie zuordnen, zu tun.

Für eine allgemeinere Lösung, überprüfen Sie Oleg's Type level lambda caclulus, die Sie Code auf der Werteebene schreiben können und dann automatisch das entsprechende Programm auf Typenebene leitet. Unglücklicherweise ist Olegs Lösung zu diesem Zeitpunkt ziemlich alt und verwendet eine nominale Implementierung des LC, die ich nicht besonders mag. Ich habe darüber nachgedacht, wie ich es besser machen könnte, aber ich halte es vielleicht aus, bis die verwertbare Gleichheit zu Typfamilien kommt.

Meine Ansicht ist, dass HLists sollte in diesen Tagen mit GADTs und DataKinds statt Tupeln erfolgen. Typfamilien sind funktionalen Abhängigkeiten vorzuziehen, sind aber derzeit begrenzter, da sie keine entscheidbare Gleichheit aufweisen.

+0

Danke. Sie sagen, es gibt mehrere Möglichkeiten, dies zu lösen - könnten Sie bitte mehr dazu sagen? Ich bin auf der Suche nach einer optimalen Lösung, es gibt also keine Notwendigkeit, sich an den bereitgestellten Code zu halten, es ist nur ein abstraktes Beispiel. Gibt es eine Möglichkeit, dies zu lösen, ohne bestimmte Instanzen für jede Funktion deklarieren zu müssen, die ich mit 'hMap' verwenden möchte? –

+0

@NikitaVolkov Ich habe allgemeinere Lösungen hinzugefügt –

1

Obwohl die folgende nicht genau die Frage beantworten (also werde ich es nicht sein zu akzeptieren), ist es das Problem nicht lösen, die die Struktur abbildet, ohne zusätzliche Instanzen für applicative functors zu erfordern:

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleInstances #-} 

import Control.Applicative 

main = do 
    print $ (hPure recursivePairs :: (Maybe Int, (Maybe Char, (Maybe Bool,())))) 
    print $ (hPure recursivePairs :: ([Int], ([Char], ([Bool],())))) 

recursivePairs :: (Int, (Char, (Bool,()))) 
recursivePairs = (1, ('a', (True,()))) 

class HPure input output where 
    hPure :: input -> output 

instance HPure()() where 
    hPure _ =() 

instance 
    (Applicative f, 
    HPure iTail oTail) => 
    HPure (iHead, iTail) (f iHead, oTail) 
    where hPure (iHead, iTail) = (pure iHead, hPure iTail) 

Ausgänge :

(Just 1,(Just 'a',(Just True,()))) 
([1],("a",([True],()))) 
Verwandte Themen