Scala隐式转换为有效宏

时间:2017-12-27 20:27:48

标签: scala dsl implicit

修改:我将问题更新为更具描述性。

注意:我使用的是Scala 2.11编译器,因为这是LMS教程项目使用的编译器版本。

我正在将用Haskell编写的DSL移植到Scala。 DSL是一种命令式语言,因此我使用带有标记的monad,即WriterT [Stmt] (State Label) a。我将此移植到Scala时遇到了麻烦,但是使用ReaderWriterState monad并使用Unit作为Reader组件来解决这个问题。然后我开始寻找Haskell中发现的do-notation的替代方案。在Scala中,For-comprehension应该是这种替代方案,但它们是针对序列而定制的,例如,无法模式匹配元组,它会插入对filter的调用。所以我开始寻找替代方案并找到了多个库:effectfulmonadlesseach。我首先尝试了effectful,这完全符合我想要实现的目标,我甚至更喜欢Haskell的符号,并且它与我曾经使用过的ReaderWriterState ScalaZ monad一起工作得很好。在我的DSL中,我有Drop()(案例类)之类的操作,我希望它能够直接用作语句。我希望对此使用含义,但因为! effectful方法(或monadless相当于此问题)的Action方法过于笼统,我无法让Scala自动转换{{1案例类为Stmt类型的东西(返回ReaderWriterState的{​​{1}})。

因此,如果不是暗示,是否会有不同的方法来实现它?

Unit所示,我确实找到了一个我不介意使用的解决方法,但我很好奇我是偶然发现了语言的某些限制,还是仅仅是我缺乏Scala的经验。

使用Main.passes2我将收到以下错误消息:未找到隐式:Main.fails1。无法取消将scalaz.Unapply[scalaz.Monad, question.Label]类型应用于按类型question.Label分类的类型M[_]的atype构造函数。检查类型类是通过编译scalaz.Monad来定义的,并查看对象implicitly[scalaz.Monad[type constructor]]中的含义,它只涵盖常见的“形状”。

来自ScalaZ的Unapplyhttps://github.com/scalaz/scalaz/blob/d2aba553e444f951adc847582226d617abae24da/core/src/main/scala/scalaz/Unapply.scala#L50

使用Unapply我会得到:值Main.fails2不是!的成员

我认为这只是编写缺少的隐式定义的问题,但我不太确定Scala希望我写哪一个。

我的build.sbt最重要的部分是版本:

question.Label

依赖项:

scalaVersion := "2.11.2",

以下是相关代码,其中包含我尝试过的内容以及运行这些内容所需的代码:

libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.11.2",
libraryDependencies += "org.scala-lang" % "scala-library" % "2.11.2",
libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.11.2",
libraryDependencies += "org.pelotom" %% "effectful" % "1.0.1",

1 个答案:

答案 0 :(得分:1)

质量问题

我想首先抱怨质量问题。您几乎没有提供您要实现的内容的文本描述,然后向我们展示一段代码,但不要明确引用您的依赖项。这远远不能算作Minimal, Complete, and Verifiable example。如果您提供易于理解和重现的明确问题,通常您会有更多机会获得答案。

返回商家

当你写

之类的东西时
unwrap(Label(unwrap(freshLabel())))

你从Scala编译器那里问得太多了。特别是unwrap只能解包包含在某些Monad中的内容,但Label不是monad。事实上,没有Monad[Label]实例,事实上它在结构上并不适合杀死你。简单地说,ScalaZ Unapply的一个实例是一个对象,允许您将应用的泛型类型MonadType[SpecificType](或其他FunctorType[SpecificType])拆分为"未应用" /&# 34;部分应用" MonadType[_]SpecificType即使(在您的情况下)MonadType[_]实际上像ReaderWriterState[Unit, List[Action], GotoLabel, _]那样复杂。因此,错误表明没有已知方法可以将Label拆分为某些MonadType[_]SpecifictType。您可能希望implicit actionStmt能够将Label自动转换为Statement,但这一步对于Scala编译器来说太过分了,因为它可以工作,它还意味着拆分复合类型。请注意,对于编译器来说,这种转换并不明显,因为unwrap本身就是一种可以处理任何Monad的通用方法。实际上在某种意义上,ScalaZ完全需要Unapply因为编译器不能自动执行这些操作。如果你通过指定泛型类型来帮助编译器,它仍然可以完成剩下的工作:

def fails1() = effectfully {
  // fails
  // unwrap(Label(unwrap(freshLabel()))) 
  // unwrap(Label(unwrap(freshLabel())))

  // works
  unwrap[Stmt](Label(unwrap(freshLabel())))
  unwrap[Stmt](Label(unwrap(freshLabel())))
}

还有另一种可能的解决方案,但它是一个非常脏的黑客:你可以推出自定义Unapply来说服Label实际上与Expr[Unit]相同的编译器分为Expr[_]Unit

  implicit def unapplyAction[AC <: Action](implicit TC0: Monad[Expr]): Unapply[Monad, AC] {
    type M[X] = Expr[X]
    type A = Unit
  } = new Unapply[Monad, AC] {
    override type M[X] = Expr[X]
    override type A = Unit

    override def TC: Monad[Expr] = TC0

    // This can't be implemented because Leibniz really witness only exactly the same types rather than some kind of isomorphism
    // Luckily effectful doesn't use leibniz implementation         
    override def leibniz: AC === Expr[Unit] = ???
  }

这是一个肮脏的黑客的明显原因是Label实际上与Expr[Unit]不同,你可以通过你无法实现{{1}这一事实来看待它在你的leibniz中。无论如何,如果您导入Unapply,即使您的原始unapplyAction也会编译并正常工作,因为有效的内部不会使用fails1

至于您的leibniz,我认为您无法以任何简单的方式使其发挥作用。可能您尝试的唯一方法是创建另一个宏,将您的fails2(或其隐式包装器)上的就地呼叫转换为!action&#39; effectful上的!。这可能有用,但我没有尝试过。