2016-08-03 2 views
6

Je construis un petit DSL en utilisant Free Monads.Est-il possible de mettre en œuvre des fonctions polymorphes dans un DSL en utilisant Free

Je voudrais pouvoir avoir des fonctions polymorphes dans mon DSL.

Un exemple de quelque chose que je voudrais construire est ceci:

{-# LANGUAGE TemplateHaskell #-} 

import   Control.Monad.Free.Church 

data Queue a = Queue a 

data MyDsl next = 
    NewQueue (Queue a -> next) | 
    WriteToQueue (Queue a) a next 

makeFree ''MyDsl 

testProgram :: F MyDsl 
testProgram = do 
    (intQueue :: Queue Int) <- newQueue 
    (charQueue :: Queue Char) <- newQueue 
    writeToQueue intQueue 1 
    writeToQueue charQueue 'c' 

La façon dont je l'ai codé ci-dessus je reçois Not in scope: type variable ‘a’ erreurs ce qui est logique. Y at-il un moyen d'avoir des fonctions polymorphes dans un DSL en utilisant Free? Pour l'arrière-plan la raison pour laquelle je voudrais faire ceci est que je puisse avoir un interprète de production qui utilise TQueue dans les coulisses et un interpréteur de test qui utilise une structure de données en mémoire pour les tests.

Répondre

11

Vous pouvez représenter votre DSL avec un GADT

{-# LANGUAGE GADTs #-} 
{-# LANGUAGE StandaloneDeriving #-} 
{-# LANGUAGE DeriveFunctor #-} 

data Queue a = Queue a 

data MyDsl next where 
    NewQueue :: (Queue a -> next) -> MyDsl next 
    WriteToQueue :: (Queue a) -> a -> next -> MyDsl next 

deriving instance Functor MyDsl 

Ni makeFree ni makeFreeCon peut générer des actions monadique polymorphiques gratuits pour MyDsl. Vous devrez les écrire vous-même.

{-# LANGUAGE FlexibleContexts #-} 

import Control.Monad.Free.Class 

newQueue :: (MonadFree MyDsl m) => m (Queue a) 
newQueue = wrap $ NewQueue return 

writeToQueue :: (MonadFree MyDsl m) => Queue a -> a -> m() 
writeToQueue q v = liftF $ WriteToQueue q v() 

Maintenant, vous pouvez écrire votre programme de test.

{-# LANGUAGE ScopedTypeVariables #-} 

import Control.Monad.Free.Church 

-- testProgram can have a more general type 
-- testProgram :: (MonadFree MyDsl m) => m() 
testProgram :: F MyDsl() 
testProgram = do 
    (intQueue :: Queue Int) <- newQueue 
    (charQueue :: Queue Char) <- newQueue 
    writeToQueue intQueue 1 
    writeToQueue charQueue 'c' 

Vous trouverez peut-être votre DSL est plus facile d'écrire plusieurs interprètes pour si vous paramétrez le type d'une file d'attente. Si vous le faites, vous aurez besoin d'une famille de types ou d'une dépendance fonctionnelle pour déterminer le type de la file d'attente à partir du type de la monade.

data MyDsl q next where 
    NewQueue :: (q a -> next) -> MyDsl next 
    WriteToQueue :: (q a) -> a -> next -> MyDsl next 
+0

Merci Cirdec c'est exactement ce que je cherchais. Bon conseil sur le paramétrage du type de file d'attente. – Brownie