2017-12-27 12 views
1

編集:私は質問をより説明的に更新しました。有効なマクロ内のモナドの値へのスカラーの暗黙的な変換

メモ:私はScala 2.11コンパイラを使用しています。これは、LMSチュートリアルプロジェクトで使用されているコンパイラのバージョンです。

私はHaskellで書かれたDSLをScalaに移植しています。 DSLは命令的言語なので、私はdo-notationを持つモナド、すなわちWriterT [Stmt] (State Label) aを使用しました。これをScalaに移植するのに問題がありましたが、ReaderWriterStateモナドを使用し、ReaderコンポーネントのUnitを使用することで問題を回避できました。私はその後、ハスケルにあるdo-notationの代替案を探しました。 For-comprehensionsはScalaのこの代替案であると考えられていますが、それらはシーケンスに合わせて調整されています。タプルにパターンが一致しない場合、filterへの呼び出しが挿入されます。だから私は選択肢を探し始め、複数のライブラリが見つかりました:effectfulmonadless、およびeachです。私はeffectfulを最初に試してみましたが、私は達成したいと思っていました。さらに、Haskellのdo-notationよりも好きで、ScalaZReaderWriterStateモナドでうまくいきました。私のDSLには、Drop()(ケースクラス)のようなアクションがあり、ステートメントとして直接使用できるようにしたいと考えています。私はこのために暗黙を使用することを望んだが、(そのことについては同等またはmonadlesseffectful!方法が一般的すぎるので、私はScalaは自動的にタイプStmtUnitを返すReaderWriterState)の何かに私のActionケースクラスを変換することができません。

したがって、暗黙でない場合、それを達成する別の方法がありますか?

Main.passes2に示されているように、私は使用しても構わない回避策を見つけましたが、私が遭遇した言語の制限があるのか​​、単にScalaの経験が足りないのか不思議です。

Main.fails1次のエラーメッセージが表示されます。Implicit not found:scalaz.Unapply[scalaz.Monad, question.Label]。型を種類クラスscalaz.Monadで分類されたタイプM[_]のatypeコンストラクタに適用解除することができません。タイプクラスが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が私に書いてほしいと思っていることは確かではありません。

私のビルドの最も重要なビット。

scalaVersion := "2.11.2", 

と依存関係:SBTバージョンです。ここ

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と数えることができないほどです。また、理解しやすく再現性のある明確な問題を提示すると、一般的に答えが得られるチャンスが増えます。

戻るビジネス

あなたはScalaのコンパイラからあまりを頼む

unwrap(Label(unwrap(freshLabel()))) 

ような何かを書くこと。特にunwrapは、何らかのMonadにラップされたものだけをアンラップできますが、Labelはモナドではありません。 Monad[Label]インスタンスが存在しないという事実だけではなく、構造的にそれがあなたを殺すように適合しないという事実です。単にScalaZ Unapplyのインスタンスは、適用されたジェネリックタイプMonadType[SpecificType](またはその他のFunctorType[SpecificType])を、適用しない/部分的に適用したMonadType[_]およびSpecificTypeに分割することを可能にするオブジェクトです(あなたの場合のように)MonadType[_]が実際にReaderWriterState[Unit, List[Action], GotoLabel, _]のような複雑なものです。したがって、LabelMonadType[_]SpecifictTypeに分割する方法は知られていません。 implicitactionStmtLabelStatementに自動的に変換するトリックですが、このステップは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()))) 
} 

あり、別の可能な解決策もあるが、それはかなり汚いハックです:あなたをロールアウトすることができますこれは汚いハックですなぜ明白な理由がLabelが実際にと同じではないということです

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として分割することができExpr[Unit]と同じであるコンパイラを説得するためのカスタムUnapplyあなたはUnapplyleibnizを実装できないという事実によってそれを見ることができます。とにかく、unapplyActionをインポートした場合、元のfails1でも効果があるのでleibnizは内部で使用されないため、コンパイルして動作します。

あなたのfails2については、私はあなたがそれを簡単な方法で動作させることはできないと思います。おそらく、試みる可能性のある唯一の方法は、action(またはその暗黙のラッパー)の!への一括呼び出しをのeffectful!に変換する別のマクロを作成することです。これはうまくいくかもしれませんが、試してみませんでした。

+0

私は、あなたが不平を言うことを正確に行おうと努力したという事実を考慮して、質問の質に関する最初のコメントをかなり不公平にしています。コードサイズを制限しますが、まだいくつかの文脈を提供するのに十分です。テキストの説明については、私は問題について尋ねます。私は特定のコードスニペットを参照しています。確かに、私は文字通りではなくコードを介してやろうとしたことを示していますが、私はそれがより明確であることを期待していました。私はbuild.sbtを提供すべきかどうか疑問に思っていましたが、十分なところで私の輸入を望んでいました。 –

+0

それを除いて、徹底的な答えをありがとう!特に、未適用の定義は、どうやってそのようにするべきかを理解するのに役立ちます。構文的なノイズを減らすことが私の目標であると考えて、私はおそらく3つ目のアプローチを試し、他のマクロを呼び出す独自のマクロを作ることができるかどうかを確認します。 –

+0

@OttidMes、質問についての部分は公平ではないかもしれませんが、私はそれに答えようとしている間に私が持っていた経験を共有しました。そして私は私の前に他の答えがないことは、あなたの質問が完全ではないことを示す兆候だと私は思う。私はまだあなたの意図のテキスト記述が** _ _ _ **であることを信じているので、私は "_それを書くよりもコードを読むのが難しいと思っています。(https://www.joelonsoftware .com/2000/04/06/things-you-should-never-do-part-i /)_ "のようになります。 – SergGr

関連する問題