2016-08-03 39 views
6

我正在使用Free Monads构建一个小型DSL。是否有可能在DSL中使用免费实现多态函数

我希望能够在我的DSL中具有多态功能。

的东西,我想建立一个例子是这样的:

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

我已经编码上面我得到Not in scope: type variable ‘a’ 错误,这是有意义的方式。有没有一种方法可以在DSL中使用免费的多态功能?

为了背景,我想这样做的原因是我可以有一个生产解释器,它在幕后使用TQueue和使用内存数据结构进行测试的测试解释器。

回答

11

您可以用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 

无论makeFree也不makeFreeCon可产生游离的多态一元的MyDsl行动是你的DSL。你需要自己写。

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

现在你可以编写你的测试程序。

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

你可能会发现你的DSL更容易编写,如果你参数队列的类型多解释。如果这样做,您需要一个类型族或函数依赖来确定monad类型的队列类型。

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

谢谢Cirdec正是我所期待的。关于参数化队列类型的好消息。 – Brownie

相关问题