编辑:我更新了问题以便更具描述性。斯卡拉隐式转换为有效宏内的一元值
注:我使用Scala 2.11编译器,因为这是LMS教程项目使用的编译器版本。
我正在将用Haskell编写的DSL移植到Scala。 DSL是一种命令式语言,所以我使用了单引号,即WriterT [Stmt] (State Label) a 。我无法将其移植到Scala,但是通过使用ReaderWriterState monad和仅使用Unit来获得Reader 组件,解决了这个问题。然后,我开始寻找Haskell中的符号替代方法。理解被认为是Scala中的这种替代方法,但它们是针对序列量身定制的,例如,无法匹配一个元组,它会插入一个调用filter 。所以我开始寻找替代品并找到了多个库:effectful ,monadless 和each 。我首先尝试了effectful ,这正是我想要达到的目标,我甚至更喜欢Haskell的do-notation,并且它与我一直使用的ScalaZ 的ReaderWriterState monad运行良好。在我的DSL中,我有类似Drop() (案例类)的操作,我希望能够直接作为语句使用。我希望用implicits这一点,但由于effectful 的! 方法(或monadless 等同为此事)太一般了,我不能让斯卡拉我Action case类自动转换为Stmt 类型(即返回Unit 的ReaderWriterState )的东西。
所以,如果没有牵连,会有不同的方式来实现它?
如Main.passes2 所示,我弄清楚了一个我不介意使用的解决方法,但我很好奇我是偶然遇到语言的一些限制,还是仅仅是我缺乏Scala的经验。
随着Main.fails1 我会收到以下错误信息:隐未找到:scalaz.Unapply[scalaz.Monad, question.Label] 。无法将类型question.Label 无法应用到按类型scalaz.Monad 分类的类型M[_] 的类型构造函数中。检查是否通过编译implicitly[scalaz.Monad[type constructor]] 来定义类型类别,并检查对象Unapply 中的含义,该对象仅涵盖常见类型的“形状”。
它来自ScalaZ其Unapply :https://github.com/scalaz/scalaz/blob/d2aba553e444f951adc847582226d617abae24da/core/src/main/scala/scalaz/Unapply.scala#L50
而且随着Main.fails2 我只会得到:值! 不是question.Label
成员,我认为它是只写失踪隐含定义的问题,但我不确定Scala希望我写的是哪一个。
我的构建中最重要的部分。SBT是版本:
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",
下面是相关的代码,包含我试过必要的东西和代码运行的那些事:
package question
import scalaz._
import Scalaz._
import effectful._
object DSL {
type GotoLabel = Int
type Expr[A] = ReaderWriterState[Unit, List[Action], GotoLabel, A]
type Stmt = Expr[Unit]
def runStmt(stmt: Stmt, startLabel: GotoLabel): (Action, GotoLabel) = {
val (actions, _, updatedLabel) = stmt.run(Unit, startLabel)
val action = actions match {
case List(action) => action
case _ => Seq(actions)
}
(action, updatedLabel)
}
def runStmt(stmt: Stmt): Action = runStmt(stmt, 0)._1
def getLabel(): Expr[GotoLabel] =
ReaderWriterState((_, label) => (Nil, label, label))
def setLabel(label: GotoLabel): Stmt =
ReaderWriterState((_, _) => (Nil, Unit, label))
implicit def actionStmt(action: Action): Stmt =
ReaderWriterState((_, label) => (List(action), Unit, label))
}
import DSL._
final case class Label(label: String) extends Action
final case class Goto(label: String) extends Action
final case class Seq(seq: List[Action]) extends Action
sealed trait Action {
def stmt(): Stmt = this
}
object Main {
def freshLabel(): Expr[String] = effectfully {
val label = getLabel.! + 1
setLabel(label).!
s"ants-$label"
}
def passes0() =
freshLabel()
.flatMap(before => Label(before))
.flatMap(_ => freshLabel())
.flatMap(after => Label(after));
def passes1() = effectfully {
unwrap(actionStmt(Label(unwrap(freshLabel()))))
unwrap(actionStmt(Label(unwrap(freshLabel()))))
}
def fails1() = effectfully {
unwrap(Label(unwrap(freshLabel())))
unwrap(Label(unwrap(freshLabel())))
}
def pasess2() = effectfully {
Label(freshLabel.!).stmt.!
Label(freshLabel.!).stmt.!
}
def fails2() = effectfully {
Label(freshLabel.!).!
Label(freshLabel.!).!
}
def main(args: Array[String]): Unit = {
runStmt(passes0())
}
}
A
回答
1
问题质量
我想先抱怨质量问题。你几乎不提供你想要实现的内容的文本描述,然后向我们展示一段代码,但不明确引用你的依赖关系。这远远不能算作Minimal, Complete, and Verifiable example。通常,如果您提供易于理解和重现的明确问题,您将有更多机会获得一些答案。
回到业务
当你喜欢写东西
unwrap(Label(unwrap(freshLabel())))
您从Scala编译器要求太多。特别是unwrap 只能解包一些Monad ,但Label 不是一个monad。这不仅仅是因为没有Monad[Label] 实例,事实上它在结构上不适合杀死你。简单来说,ScalaZ Unapply 的一个实例是一个对象,允许您将应用的泛型类型MonadType[SpecificType] (或其他FunctorType[SpecificType] )拆分为“未应用”/“部分应用”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] ,你可以看到它,因为你不能在你的Unapply 中实现leibniz 。无论如何,如果您导入unapplyAction ,即使您的原始fails1 也会编译并工作,因为有效的内部不会使用leibniz 。
至于你的fails2 ,我认为你不能以任何简单的方式工作。可能您尝试的唯一方法是创建另一个宏,将您的action (或其隐式包装器)上的! 就地调用转换为action.stmt 上的effectful 的! 。这可能工作,但我没有尝试。
|