2016-08-03 46 views
6

Buduję małe DSL za pomocą Free Monads.Czy jest możliwe zaimplementowanie funkcji polimorficznych w DSL przy użyciu Free

Chciałbym mieć funkcje polimorficzne w moim DSL.

Przykładem coś chciałbym zbudować to:

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

sposób mam zakodowane go powyżej mam Not in scope: type variable ‘a’ błędy, które ma sens. Czy istnieje sposób na funkcje polimorficzne w DSL za pomocą Free?

Dla tła powód, dla którego chciałbym to zrobić, jest taki, że mogę mieć interpreter produkcyjny, który używa TQueue za sceną i interpretera testowego, który wykorzystuje strukturę danych w pamięci do testowania.

Odpowiedz

11

Można reprezentować DSL z 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 

Ani makeFree ani makeFreeCon może generować wolne polimorficznych działań monadycznych dla MyDsl. Musisz napisać je sam.

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

Teraz możesz napisać swój program testowy.

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

można znaleźć DSL jest łatwiej napisać wiele tłumaczy dla jeśli parametryzacji typ kolejki. Jeśli to zrobisz, będziesz potrzebować rodziny typów lub zależności funkcjonalnej, aby określić typ kolejki od typu monady.

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

Dziękuję Cirdec, że dokładnie to, czego szukałem. Dobra wskazówka dotycząca parametryzowania typu kolejki. – Brownie