Scala 自由单子中的抽象结果类型

Scala 自由单子中的抽象结果类型,scala,haskell,functional-programming,free-monad,Scala,Haskell,Functional Programming,Free Monad,假设我们想要定义一个简单的DSL来定义UI交互,在这里我们可以创建对象,然后选择它们: object TestCommand { sealed trait EntityType case object Project extends EntityType case object Site extends EntityType sealed trait TestCommand[A, E] case class Create[A, E](entityType: EntityT

假设我们想要定义一个简单的DSL来定义UI交互,在这里我们可以创建对象,然后选择它们:

object TestCommand {

  sealed trait EntityType
  case object Project extends EntityType
  case object Site extends EntityType

  sealed trait TestCommand[A, E]
  case class Create[A, E](entityType: EntityType, withEntity: E => A) extends  TestCommand[A, E]
  case class Select[A, E](entity: E, next: A) extends TestCommand[A, E]

} 
我的问题是,我不想指定创建命令的返回类型应该是什么(
E
)。我想让翻译来决定这个问题。例如,
E
可以是一个字符串,如果我们使用异步REST调用创建对象,则可以是一个
Future

如果我尝试使用
liftF
以常规方式定义DSL,如下所示:

object TestDSL {

  def create[E](entityType: EntityType): Free[TestCommand[?, E], E] =
    Free.liftF(Create(entityType, identity: E => E): TestCommand[E, E])

  def select[E](entity: E): Free[TestCommand[?, E], Unit] =
    Free.liftF(Select[Unit, E](entity, ()))

}
我得到以下错误:

Error:(10, 10) no type parameters for method liftF: (value: S[A])scalaz.Free[S,A] exist so that it can be applied to arguments (dsl.TestCommand.TestCommand[E,E])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : dsl.TestCommand.TestCommand[E,E]
 required: ?S[?A]
    Free.liftF(Create(entityType, identity: E => E): TestCommand[E, E])
我无法理解上面代码中的错误,但更重要的问题是,这是否是对自由单子中出现的类型进行抽象的正确方法。如果没有,正确的(功能性)方法是什么

编辑:

在Haskell中,上述方法毫无问题:

{-# LANGUAGE DeriveFunctor #-}
-- |

module TestDSL where

import           Control.Monad.Free

data EntityType = Project | Site

data TestCommand e a = Create EntityType (e -> a) | Select e a
  deriving Functor

-- | The DSL
create :: EntityType -> Free (TestCommand e) e
create et = liftF $ Create et id

select :: e -> Free (TestCommand e) ()
select e = liftF $ Select e ()


-- | A sample program:
test :: Free (TestCommand e) ()
test = do
  p <- create Project
  select p
  _ <- create Site
  return ()

-- | A trivial interpreter.
interpTestCommand :: TestCommand String a -> IO a
interpTestCommand (Create Project withEntity) = do
  putStrLn $ "Creating a project"
  return (withEntity "Project X")
interpTestCommand (Create Site withEntity) = do
  putStrLn $ "Creating a site"
  return (withEntity "Site 51")
interpTestCommand (Select e next) = do
  putStrLn $ "Selecting " ++ e
  return next

-- | Running the interpreter
runTest :: IO ()
runTest = foldFree interpTestCommand test

现在您有了
test::Free(TestCommand e)(
)。这意味着实体
e
的类型可以是调用者想要的任何类型,但在整个计算过程中它是固定的

但那是不对的!在现实世界中,响应
创建
命令而创建的实体类型取决于命令本身:如果创建了
项目
,则
e
应该是
项目
;如果您创建了一个
站点
,那么
e
应该是
站点
。因此,
e
不应该在整个计算过程中固定不变(因为我可能想创建
项目
s和
站点
s),调用方也不应该选择
e

下面是一个解决方案,其中实体的类型取决于命令的值

data Site=Site{{-…-}
数据项目=项目{{-…-}
数据实体类型e,其中
SiteTy::EntityType站点
ProjectTy::EntityType项目
这里的想法是
实体类型e
上的模式匹配告诉您它的
e
是什么。在
Create
命令中,我们将存在性地打包一个实体
e
以及一点形式为
EntityType e
的GADT证据,您可以通过模式匹配来了解
e
是什么

数据命令,其中
Create::EntityType e->(e->r)->CommandF r
选择::EntityType e->e->r->CommandF r
实例函子CommandF,其中
fmap f(创建t下一个)=创建t(f下一个)
fmap f(选择下一步)=选择下一步
类型命令=自由命令f
create::EntityType e->Command e
创建t=自由(创建t纯)
选择::EntityType e->e->Command()
选择te=Free(选择te(纯())
myComputation::Command()
myComputation=do

p感谢您指出设计缺陷,以及使用GADT的好例子!我看到的问题是,您仍然在免费monad上决定什么是
站点
项目
。我想要的是把这个决定推迟到译员那里。另一方面,由于Scala缺乏对GADT的支持,并且考虑到替代方案是编写DSL OO风格,我想知道这个设计缺陷有多严重(我认为Java不会提供更好的类型安全性)。我想我不理解您的用例。在我看来,如果您的应用程序层(
CommandF
)知道存在两个名为
Site
Project
的实体,那么它可能应该知道它们看起来像什么。这就像我在问题中所说的:如果我通过rest调用实现实体的创建,那么这些实体可能是(
Project
Site
)是未来。如果我只想模拟行为,这些实体可以是随机字符串。这就是为什么我想推迟选择特定实体。例如,如果我选择
data Project=Project{uuid::String}
,似乎我做了太多的假设(但就所有实际目的而言,这可能并不实际……)。例如,上面的选择意味着我无法传递
Future
Try
周围…您真的需要嵌入式语言中的并发支持吗?如果您决定这样做,只需向基本functor添加一个新构造函数:
concurrent::r->r->CommandF r
。解释器将负责决定h如何同时运行这两个子计算。一天结束时,如果嵌入式语言实际上不能处理它要处理的对象,那么它就不是很有用。您的应用程序逻辑必须了解
站点和
项目,因为操作
站点是应用程序的工作还有
Project
s!同样地,解释器的工作是处理HTTP和并发以及所有那些乱七八糟的现实世界的东西。
λ> runTest
Creating a project
Selecting Project X
Creating a site