2017-12-27 573 views
1

編輯:我更新了問題以便更具描述性。斯卡拉隱式轉換爲有效宏內的一元值

注:我使用Scala 2.11編譯器,因爲這是LMS教程項目使用的編譯器版本。

我正在將用Haskell編寫的DSL移植到Scala。 DSL是一種命令式語言,所以我使用了單引號,即WriterT [Stmt] (State Label) a。我無法將其移植到Scala,但是通過使用ReaderWriterState monad和僅使用Unit來獲得Reader組件,解決了這個問題。然後,我開始尋找Haskell中的符號替代方法。理解被認爲是Scala中的這種替代方法,但它們是針對序列量身定製的,例如,無法匹配一個元組,它會插入一個調用filter。所以我開始尋找替代品並找到了多個庫:effectful,monadlesseach。我首先嚐試了effectful,這正是我想要達到的目標,我甚至更喜歡Haskell的do-notation,並且它與我一直使用的ScalaZReaderWriterState monad運行良好。在我的DSL中,我有類似Drop()(案例類)的操作,我希望能夠直接作爲語句使用。我希望用implicits這一點,但由於effectful!方法(或monadless等同爲此事)太一般了,我不能讓斯卡拉我Action case類自動轉換爲Stmt類型(即返回UnitReaderWriterState)的東西。

所以,如果沒有牽連,會有不同的方式來實現它?

Main.passes2所示,我弄清楚了一個我不介意使用的解決方法,但我很好奇我是偶然遇到語言的一些限制,還是僅僅是我缺乏Scala的經驗。

隨着Main.fails1我會收到以下錯誤信息:隱未找到:scalaz.Unapply[scalaz.Monad, question.Label]。無法將類型question.Label無法應用到按類型scalaz.Monad分類的類型M[_]的類型構造函數中。檢查是否通過編譯implicitly[scalaz.Monad[type constructor]]來定義類型類別,並檢查對象Unapply中的含義,該對象僅涵蓋常見類型的「形狀」。

它來自ScalaZ其Unapplyhttps://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()) 
    } 
} 

回答

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。你可能希望你的implicitactionStmt會做的伎倆來自動轉換LabelStatement但是這一步是太多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!。這可能工作,但我沒有嘗試。

+0

我覺得你對問題質量的第一個評論很不公平,考慮到我努力去做你所抱怨的事情。限制代碼大小,但仍提供足夠的空間來提供一些上下文。關於文本描述,我問了這個問題,我提到了一個特定的代碼片段,在文本中也沒有提及這一點。誠然,我展示了我試圖做的不是文本上的,而是通過代碼,但我希望更清楚。我懷疑我是否應該提供build.sbt,但希望我的進口清楚。 –

+0

那邊,謝謝你徹底的回答!特別是不適用的定義有助於理解我應該如何去做。考慮到我的目標是減少語法噪音,我可能會嘗試你的第三種方法,看看我是否可以製作自己的調用其他宏的宏。 –

+0

@OttidMes,關於這個問題的部分可能不公平,但我分享了我在嘗試回答時的經驗。而且我認爲在我之前缺乏其他答案是一個跡象,表明你的問題遠非完美。我仍然堅信,對你的意圖的文本描述是任何非平凡問題的必須要求,因爲我相信「[閱讀代碼比編寫代碼更難。](https://www.joelonsoftware .COM/2000/04/06 /東西 - 你 - 應該 - 永遠-DO部分-I /)_」。 – SergGr