2012-02-11 15 views
13

sto cercando di costruire una funzione di tipo:sollevamento una funzione di ordine superiore in Haskell

liftSumthing :: ((a -> m b) -> m b) -> (a -> t m b) -> t m b 

dove t è un trasformatore monade. In particolare, io sono interessato a fare questo:

liftSumthingIO :: MonadIO m => ((a -> IO b) -> IO b) -> (a -> m b) -> m b 

I giocherellava con alcune librerie wizardry Haskell e ma senza alcun risultato. Come ottenerlo giusto, o forse c'è una soluzione pronta da qualche parte che non ho trovato?

+1

Gah! perché tutte le domande di Haskell sono così difficili? Nessun punto facile qui :-( – drozzy

+3

@drozzy: questo tag in realtà [ha uno dei più alti numeri medi di voti per risposta] (http://data.stackexchange.com/stackoverflow/query/61353/tags-with-highest-average -answer-scores-between-tags-with-at-least-1000-questions), quindi mentre potrebbero non essere sempre facili, le persone vengono premiate per i loro sforzi – hammar

risposta

18

Questo non può essere fatto genericamente su tutte le istanze MonadIO a causa del tipo IO in una posizione negativa. Ci sono alcune librerie su hackage che fanno questo per istanze specifiche (monad-control,), ma c'è stato qualche dibattito sul fatto che siano semanticamente valide, specialmente per quanto riguarda il modo in cui gestiscono le eccezioni e le strane cose simili IO.

Modifica: alcune persone sembrano interessate alla distinzione posizione positiva/negativa. In realtà, non c'è molto da dire (e probabilmente lo hai già sentito, ma con un nome diverso). La terminologia viene dal mondo dei sottotitoli.

L'intuizione dietro sottotipaggio è che "a è un sottotipo di b (che io scriverò a <= b) quando un a può essere utilizzato ovunque un b ci si aspettava, invece". Decidere la sottotipizzazione è semplice in molti casi; per i prodotti, (a1, a2) <= (b1, b2) ogni volta che a1 <= b1 e a2 <= b2, ad esempio, è una regola molto semplice. Ma ci sono alcuni casi complicati; per esempio, quando dovremmo decidere che a1 -> a2 <= b1 -> b2?

Bene, abbiamo una funzione f :: a1 -> a2 e un contesto prevede una funzione di tipo b1 -> b2. Quindi il contesto utilizzerà il valore di ritorno di f come se fosse un b2, quindi è necessario che sia a2 <= b2. La cosa delicata è che il contesto fornirà f con un b1, anche se lo f lo userà come se fosse uno a1. Quindi, dobbiamo richiedere che sia b1 <= a1 - che guarda all'indietro da ciò che potresti indovinare! Diciamo che a2 e b2 sono "covarianti" o si verificano in una "posizione positiva" e a1 e b1 sono "controvarianti" o si verificano in una "posizione negativa".

(Quick parte: perché? "Positivo" e "negativo" E 'motivato dalla moltiplicazione Prendere in considerazione questi due tipi:.

f1 :: ((a1 -> b1) -> c1) -> (d1 -> e1) 
f2 :: ((a2 -> b2) -> c2) -> (d2 -> e2) 

Quando dovrebbe f1 'tipo s essere un sottotipo di f2' di tipo s io? dichiarare questi fatti (esercizio: controllare questo utilizzando la regola di cui sopra):

  • dovremmo avere e1 <= e2
  • dovremmo avere d2 <= d1
  • 01.235.164,106 mila..
  • Dovremmo avere c2 <= c1.
  • Dovremmo avere b1 <= b2.
  • Dovremmo avere a2 <= a1.

e1 è in una posizione positiva in d1 -> e1, che è a sua volta in una posizione positiva nel tipo di f1; inoltre, e1 è in una posizione positiva nel tipo di f1 in generale (poiché è covariante, per il fatto sopra). La sua posizione nell'intero termine è il prodotto della sua posizione in ogni sottotema: positivo * positivo = positivo. Allo stesso modo, d1 si trova in una posizione negativa in d1 -> e1, che si trova in una posizione positiva nell'intero tipo. negativo * positivo = negativo e le variabili d sono effettivamente controvarianti. b1 è in una posizione positiva nel tipo a1 -> b1, che è in una posizione negativa in (a1 -> b1) -> c1, che è in una posizione negativa nell'intero tipo. positivo * negativo * negativo = positivo, ed è covariante. Si ottiene l'idea)

Ora, diamo uno sguardo alla classe di MonadIO:.

class Monad m => MonadIO m where 
    liftIO :: IO a -> m a 

Possiamo vedere questo come una dichiarazione esplicita di sottotipo: stiamo dando un modo per fare IO a essere un sottotipo di m a per un po 'di cemento m. Sappiamo subito che possiamo prendere qualsiasi valore con i costruttori IO in posizioni positive e trasformarli in m s. Ma questo è tutto: non abbiamo modo di trasformare i costruttori IO negativi in ​​m s - abbiamo bisogno di una classe più interessante per questo.

+7

"a causa del tipo' IO' in una posizione negativa "- puoi approfondire cosa significa e perché è significativo? –

+2

@DanBurton Ne ho scritto un po '. –

+0

Questo è certamente utile. Penso che il controllo di monad potrebbe permettermi di farlo con abbastanza armeggiare. non vedo come il cambio di contesto da 'ma' a' tma' spezzerebbe qualcosa in questo caso.A causa della mancanza di altre risposte sto impostando ciò come accettato. – zeus

Problemi correlati