2012-03-22 6 views
2

Existe-t-il un moyen d'écrire une fonction f :: (a -> b -> ... -> t) -> (Monad m => m a -> m b -> ... -> m t), fondamentalement liftMn pour tout n?Ecriture d'une fonction (a -> b -> ... -> t) -> (Monad m => ma -> mb -> ... -> mt)

(EDIT: exemple de sens fixe.)

Je vous écris une bibliothèque de FRP, et je pensais que ce serait propre si je pouvais avoir le code vaguement:

main = do 
    input1 <- signalFromTextBoxTheUserMayTypeANumberInto 
    input2 <- signalFromAnotherTextBox 
    divided <- signal div input1 input2 
    signal (\x -> showTextAlert ("input1 `div` input2 is " ++ show x)) divided 

Je suis tripoter avec des familles de types pour le faire fonctionner, mais je commence à penser que ce n'est pas réalisable. Je suis actuellement en train de faire quelque chose comme ceci:

type Action a = IORef a -> IO() 
type Listener = IO() 
newtype Signal a = Signal (IORef (SigData a)) 

data SigData a = SigData { 
    trigger :: Action a, 
    output :: IORef a, 
    listeners :: [Listener] 
    } 

class Sig a where 
    type S a 
    mkSig :: [AnySignal] -> a -> S a 

instance Sig b => Sig (a -> b) where 
    type S (a -> b) = Signal a -> S b 
    mkSig dependencies f = 
    \[email protected](Signal sig) -> 
     let x = unsafePerformIO $ readIORef sig >>= readIORef . output 
     in mkSig (AnySignal s : dependencies) (f x) 

instance Sig Int where 
    type S Int = IO (Signal Int) 
    out <- newIORef x 
    self <- Signal <$> (newIORef $ SigData { 
     trigger = \ref -> writeIORef ref $! x, 
     output = out, 
     listeners = [] 
    }) 
    mapM_ (self `listensTo`) deps 
    return self 

Cela ne fonctionne évidemment pas, comme le unsafePerformIO s'évalué une fois et conserve ensuite cette valeur, et si elle ne fonctionnait serais toujours laid, aki et généralement mal . Y a-t-il un moyen de le faire, ou devrais-je simplement abandonner l'idée?

+0

Demandez-vous: Est-ce que je peux donner un type raisonnable à ce combinateur? Sinon, quel type de magie noire est nécessaire pour lui donner un type raisonnable? Cela pourrait être utile: [Comment créer une fonction haskell polyvariadique?] (Http://stackoverflow.com/q/3467279/417501) – fuz

Répondre

10

Je suis un peu nouveau dans tout cela, alors pardonnez-moi si c'est une réponse idiote, mais n'est-ce pas exactement ce que sont les foncteurs applicatifs?

vous permettent de faire applicatifs quelque chose comme:

f :: a -> b -> ... -> c 

f2 :: Applicative p => p a -> p b ... -> p c 
f2 x ... y = f <$> x <*> ... <*> y 

si je ne me trompe pas. (Les ellipses sont un nombre quelconque de types/arguments)

+1

En effet. L'exemple donné serait 'divisé <- div <$> input1 <*> input2'. Les foncteurs applicatifs sont idéaux pour les PRF; les bibliothèques [reactive-banana] (http://hackage.haskell.org/package/reactive-banana) et [sodium] (http://hackage.haskell.org/package/sodium) adoptent toutes deux cette approche. – ehird

+0

Oui, c'est le plan de sauvegarde si je n'arrive pas à faire fonctionner ce type de supercherie. Ce n'est pas aussi joli que ça. :( – valderman

+2

@valderman, la tricherie de type ne vous causera que des ennuis, cela a été fait plusieurs fois, et échoue dans tous les cas sauf les cas les plus concrets (c'est-à-dire qu'il détruit votre capacité d'abstraction). Je les recommande (et vous apprendrez probablement à le trouver plus joli que ce que vous suggérez une fois que vous aurez une idée de la structure algébrique) – luqui

6

Que diriez-vous du préprocesseur Strathclyde Haskell Environment, qui vous permet d'utiliser idiom brackets, la notation originale pour les foncteurs applicatifs? Cela vous permet d'utiliser (| f a b c |) pour f <$> a <*> b <*> c. Votre exemple serait (| input1 `div` input2 |). Au fait, c'est probablement une mauvaise idée pour votre type Signal d'avoir une instance Monad; cela provoque le problème bien connu (dans la communauté FRP) de temps fuit; Voir this blog post pour plus d'informations. Une interface Applicative est OK, mais une interface Monad ne l'est pas. Il existe plusieurs solutions qui empêchent les fuites de temps tout en permettant le même comportement de changement d'événement dynamique, impliquant des choses comme an additional type parameter ou une autre monade (comme vu dans, par exemple, la bibliothèque sodium).

+0

Ce serait une amélioration par rapport à la notation _ <*> partout, en effet. l'instance de Monad! – valderman

Questions connexes