2016-08-03 38 views
6

zu implementieren Ich baue ein kleines DSL mit Free Monads.Ist es möglich, polymorphe Funktionen in einem DSL mit Free

Ich möchte in meinem DSL polymorphe Funktionen haben können.

Ein Beispiel für etwas, das ich ist dies bauen mag:

{-# 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' 

Das ich es oben Ich Not in scope: type variable ‘a’ Fehler erhalten codierten Weise

, die Sinn macht. Gibt es einen Weg, polymorphe Funktionen in einem DSL mit Free zu haben?

Für Hintergrund der Grund, den ich dies tun möchte, ist, damit ich einen Produktionsinterpreter haben kann, der TQueue hinter den Kulissen und einen Testinterpreter verwendet, der eine In-Memory-Datenstruktur zum Testen verwendet.

Antwort

11

Sie können Ihren DSL mit GADT repräsentieren

{-# 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 

Weder makeFree noch makeFreeCon freien polymorphen monadischen Aktionen für MyDsl erzeugen kann. Sie müssen sie selbst schreiben.

{-# 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() 

Jetzt können Sie Ihr Testprogramm schreiben.

{-# 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' 

Sie Ihr DSL ist einfacher finden können für mehrere Dolmetscher zu schreiben, wenn Sie den Typ einer Warteschlange parametrieren. Wenn Sie dies tun, benötigen Sie eine Typfamilie oder funktionale Abhängigkeit, um den Queue-Typ vom Typ der Monade zu bestimmen.

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

Vielen Dank Cirdec, das ist genau das, was ich gesucht habe. Netter Tipp zum Parametrieren des Queue-Typs. – Brownie