繁体   English   中英

Free Monads中的抽象结果类型

[英]Abstract result types in Free Monads

假设我们要定义一个简单的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 )。 我想让这个决定由口译员决定。 例如,如果我们使用异步REST调用创建对象,则E可以是字符串,也可以是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])

我无法理解上面代码中出了什么问题,但更重要的一个问题是,这是否是抽象出现在免费monad中的类型的正确方法。 如果没有,什么是正确的(功能)方法?

编辑

在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

运行测试将产生以下输出:

λ> runTest
Creating a project
Selecting Project X
Creating a site

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

但那不对! 在现实世界中,为响应Create命令而创建的实体的类型取决于命令本身:如果您创建了一个Project那么e应该是Project ; 如果你创建了一个Site那么e应该是Site 所以e不应该被固定在整个计算(因为我可能需要创建Project S Site S),并且它不应该是由调用者挑选的e

这是一种解决方案,其中实体的类型取决于命令的值。

data Site = Site { {- ... -} }
data Project = Project { {- ... -} }

data EntityType e where
    SiteTy :: EntityType Site
    ProjectTy :: EntityType Project

这里的想法是EntityType e上的模式匹配告诉你它的e是什么。 Create命令中,我们将存在性地打包实体e以及一些形式为EntityType e的GADT证据,您可以对其进行模式匹配以了解e是什么。

data CommandF r where
    Create :: EntityType e -> (e -> r) -> CommandF r
    Select :: EntityType e -> e -> r -> CommandF r

instance Functor CommandF where
    fmap f (Create t next) = Create t (f . next)
    fmap f (Select t e next) = Select t e (f next)

type Command = Free CommandF

create :: EntityType e -> Command e
create t = Free (Create t Pure)

select :: EntityType e -> e -> Command ()
select t e = Free (Select t e (Pure ()))

myComputation :: Command ()
myComputation = do
    p <- create ProjectTy  -- p :: Project
    select ProjectTy p
    s <- create SiteTy  -- s :: Site
    return ()

当解释器到达Create指令时,其作用是返回与包装的EntityType匹配的类型的实体。 它必须检查EntityType以了解e是什么并且行为恰当。

-- assuming createSite :: IO Site and createProject :: IO Project

interp :: CommandF a -> IO a
interp (Create SiteTy next) = do
    site <- createSite
    putStrLn "created a site"
    return (next site)
interp (Create ProjectTy next) = do
    project <- createProject
    putStrLn "created a project"
    return (next project)
-- plus clauses for Select

我不知道这会如何完全转化为Scala,但这是Haskell中的主旨。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM