是否可以使用 Free 在 DSL 中实现多态函数
Is it possible to implement polymorphic functions in a DSL using Free
我正在使用 Free Monads 构建一个小型 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’
有道理的错误。有没有办法使用 Free 在 DSL 中拥有多态函数?
作为背景,我想这样做的原因是我可以有一个在幕后使用 TQueue 的生产解释器和一个使用内存数据结构进行测试的测试解释器。
你可以用 GADT 代表你的 DSL
{-# 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
生成自由的多态单子动作。你需要自己写。
{-# 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
我正在使用 Free Monads 构建一个小型 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’
有道理的错误。有没有办法使用 Free 在 DSL 中拥有多态函数?
作为背景,我想这样做的原因是我可以有一个在幕后使用 TQueue 的生产解释器和一个使用内存数据结构进行测试的测试解释器。
你可以用 GADT 代表你的 DSL
{-# 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
生成自由的多态单子动作。你需要自己写。
{-# 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