5

次のようなワークフローがあります。

parse template -> check consistency
                                    -> check conformance of one template to another
parse template -> check consistency

これらの手順のいずれかが失敗する可能性があります。私はそれを Scala で実装したいと思います。できれば、並列ブランチが独立して評価され、両方のエラーがマージされるようにします。おそらくモナド スタイルですが、一般的な OOP パターンにも興味があります。現在、このようなチェーンを使用して、さまざまなアクション用にハードコードされた複数のバリエーションがあります

def loadLeftTemplateAndForth (leftPath : String, rightPath : String) = {
  val (template, errors) = loadTemplate(leftPath)
  if(errors.isEmpty) loadRightTemplateAndForth(template, rightPath)
  else popupMessage("Error.")
}

これはある種のアンチパターンに違いないと思います。ステップはワークフローから分離する必要がありますが、非常に洗練されたものを思い付くことができず、すでに証明された方法があるはずです.

編集: わかりましたので、このようなものを実装しようとして失敗しました

(((parseTemplate(path1) :: HNil).apply(checkConsistency _) :: ((parseTemplate(path2) :: HNil).apply(checkConsistency _)) :: HNil).apply(checkConformance _)

def checkConformance (t1 : Template)(t2 : Template) : Seq[Error]

関数は、成功 (結果) または失敗 (エラー) を返します。私は HLists を使用していましたが、型推論規則やその他の問題で迷子になりました。かなり近かったみたいですけど。このことを知っている人にとっては、おそらく簡単なことでしょう。

編集: 私はついにこれを実装することができました

(parseTemplate("Suc") :: Args).apply(checkConsistency _) :: 
(parseTemplate("Suc") :: Args).apply(checkConsistency _) :: Args)
.apply(checkConformance _)

各関数は、Either と同等のものを返さなければならず、適用された関数のエラー型は引数のエラー型のサブタイプでなければならないといういくつかの unfornate 制約があります。HList、アプリケーションの型クラス、ラッパー クラスの Successful/UnsuccessfulArgList を使用して実行しました。

4

3 に答える 3

1

メソッドloadTemplateに が返されるようにしEither[List[String], Template]ます。

エラーの場合は を返しLeft(List("error1",...))、成功の場合は を返しRight(template)ます。

それからあなたはすることができます

type ELT = Either[List[String], Template]

def loadTemplate(path: String): ELT = ...

def loadRightTemplateAndForth(template: Template, rightPath: String): ELT = ...

def loadLeftTemplateAndForth(leftPath: String, rightPath: String): ELT =
  for {
    lt <- loadTemplate(leftPath).right
    rt <- loadRightTemplateAndForth(lt, rightPath).right
  } yield rt

上記は「フェイル ファスト」です。つまり、2 つのブランチからのエラーはマージされません。最初が失敗した場合、 a が返さLeftれ、2 番目は評価されません。を使用してエラーの蓄積を処理するコードについては、このプロジェクトを参照してくださいEither

あるいは、Scalaz Validation を使用できます。適切な説明については、内包表記とモナドを使用した Scala でのメソッド パラメーターの検証を参照してください。

于 2013-04-01T20:32:05.533 に答える
1

これはどう?

// Allows conditional invocation of a method
class When[F](fun: F) {
    def when(cond: F => Boolean)(tail: F => F) = 
      if (cond(fun)) tail(fun) else fun
}
implicit def whenever[F](fun: F): When[F] = new When[F](fun)

その後:

parseTemplate(t1).when(consistent _){ 
  val parsed1 = _
  parseTemplate(t2).when(consistent _){ 
    conforms(parsed1, _) 
  }
}

エラーのホルダーを作成し、(parseTemplate、consistent、conforms に) 渡すか、ThreadLocal を使用します。

ここでは、さらに分離されています。

(parseTemplate(t1), parseTemplate(t2))
  .when(t => consistent(t._1) && consistent(t._2)){ t =>
    conforms(t._1, t._2) 
  }

編集

私はこのようなものになってしまいました:

def parse(path: String): Either[
  String,  // error
  AnyRef   // result
] = ?

def consistent(result: Either[String, AnyRef]): Either[
  String,  // error
  AnyRef   // result
] = ?

def conforms(result1: Either[String, AnyRef], result2: Either[String, AnyRef], 
  fullReport: List[Either[
    List[String],  // either list of errors 
    AnyRef         // or result
  ]]): List[Either[List[String], AnyRef]] = ?

( (parse("t1") :: Nil).map(consistent _), 
  (parse("t2") :: Nil).map(consistent _)
).zipped.foldLeft(List[Either[List[String], AnyRef]]())((fullReport, t1t2) =>
  conforms(t1t2._1, t1t2._2, fullReport))
于 2013-03-31T23:41:23.387 に答える
0

したがって、私がなんとかそれを行う方法は次のとおりです(ただし、リストエラーと関数エラーに共通のタイプのエラーのシーケンスを構築するなど、改良を使用することもできます):

HList.scala

import HList.::

sealed trait HList [T <: HList[T]] {

  def ::[H1](h : H1) : HCons[H1, T]

}

object HList { 

  type ::[H, T <: HList[T]] = HCons[H, T] 

  val HNil = new HNil{}

}

final case class HCons[H, T <: HList[T]](head: H, tail: T) extends HList[HCons[H, T]] {

  override def ::[H1](h: H1) = HCons(h, this)

  def apply[F, Out](fun : F)(implicit app : HApply[HCons[H, T], F, Out]) = app.apply(this, fun)

  override def toString = head + " :: " + tail.toString

  None
}

trait HNil extends HList[HNil] {
  override def ::[H1](h: H1) = HCons(h, this)
  override def toString = "HNil"
}

HListApplication.scala

@implicitNotFound("Could not find application for list ${L} with function ${F} and output ${Out}.")
trait HApply[L <: HList[L], -F, +Out] {
  def apply(l: L, f: F): Out
}

object HApply {

  import HList.::

  implicit def happlyLast[H, Out] = new HApply[H :: HNil, H => Out, Out] {
    def apply(l: H :: HNil, f: H => Out) = f(l.head)
  }

  implicit def happlyStep[H, T <: HList[T], FT, Out](implicit fct: HApply[T, FT, Out]) = new HApply[H :: T, H => FT, Out] {
    def apply(l: H :: T, f: H => FT) = fct(l.tail, f(l.head))
  }

}

ErrorProne.scala

sealed trait ErrorProne[+F, +S]

case class Success [+F, +S] (result : S) extends ErrorProne[F, S]

case class Failure [+F, +S] (errors : Seq[F]) extends ErrorProne[F, S]

ArgList.scala

import HList.::
import HList.HNil

sealed trait ArgList [E, L <: HList[L]] {

  def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]]) 
  : ErrorProne[E, S]

  def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L]

}

case class SuccessArgList [E, L <: HList[L]] (list : L) extends ArgList[E, L] {

  def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]]) 
  : ErrorProne[E, S] = app.apply(list, fun)

  override def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L] = argument match {
    case Success(a) => SuccessArgList(a :: list)
    case Failure(e) => FailureArgList(e)
  }

}

case class FailureArgList [E, L <: HList[L]] (errors : Seq[E]) extends ArgList[E, L] {

  def apply[F, S](fun : F)(implicit app : HApply[L, F, ErrorProne[E, S]]) 
  : ErrorProne[E, S] = Failure(errors)

  override def :: [A, E1 <: EX, EX >: E] (argument : ErrorProne[E1, A]) : ArgList[EX, A :: L] = argument match {
    case Success(a) => FailureArgList(errors)
    case Failure(newErrors) => FailureArgList(Seq[EX]() ++ errors ++ newErrors)
  }

}

object Args {

  def :: [E1, A] (argument : ErrorProne[E1, A]) : ArgList[E1, A :: HNil] = argument match {
    case Success(a) => SuccessArgList(a :: HNil)
    case Failure(e) => FailureArgList(e)
  }

}

使用法

val result = ((parseTemplate("Suc") :: Args).apply(checkConsistency _) :: 
              (parseTemplate("Suc") :: Args).apply(checkConsistency _) :: Args)
              .apply(checkConformance _)

trait Err
case class Err1 extends Err
case class Err2 extends Err
case class Err3 extends Err

def parseTemplate(name : String) : ErrorProne[Err, Int] = if(name == "Suc") Success(11) else Failure(Seq(Err1()))

def checkConsistency(value : Int) : ErrorProne[Err2, Double] = if(value > 10) Success(0.3) else Failure(Seq(Err2(), Err2()))

def checkConformance(left : Double) (right : Double) : ErrorProne[Err3, Boolean] = 
    if(left == right) Success(true) else Failure(Seq(Err3()))
于 2013-04-02T15:51:00.493 に答える