代码之家  ›  专栏  ›  技术社区  ›  AdrianS

免费单子口译员

  •  0
  • AdrianS  · 技术社区  · 7 年前

    我一直在做一个练习,试图用自由单子实现一个基本的计算器。 据我所知,自由单子的目的和我想要实现的是: 写我的程序(数学表达式)一旦运行它与不同的口译员。 现在我不确定我是否做了100%的惯用实现,至少是因为:

    我的程序需要在泛型A上参数化,它应该与解释器上下文匹配。

    def program[A] = for {
          two <- lit[A](2)
          four <- lit[A](4)
          sum <- add(two, four)
        } yield sum
    
    program[Int].foldMap(eval) shouldBe 6
    program[String].foldMap(print) shouldBe "(2 + 4)"
    
    import cats.instances.option._
    program[Option[Int]].foldMap(evalOpt) shouldBe Option(6)
    

    ADT/代数和“智能构造器”

    trait Expression2[A] extends Product with Serializable
      case class Lit[A](a: Int) extends Expression2[A]
      case class Add[A](a: A, b: A) extends Expression2[A]
      case class Mult[A](a: A, b: A) extends Expression2[A]
    
      type ExprAlg[B] = Free[Expression2, B]
    
      def lit[A](a: Int): ExprAlg[A] = Free.liftF(Lit(a))
      def add[A](a: A, b: A): ExprAlg[A] = Free.liftF(Add(a, b))
      def mult[A](a: A, b: A): ExprAlg[A] = Free.liftF(Mult(a, b))
    

    数学解释器:

    def eval: Expression2 ~> Id = new (Expression2 ~> Id) {
        override def apply[A](fa: Expression2[A]): Id[A] = eval(fa).asInstanceOf[A]
    
        def eval[A](expression2: Expression2[A]): Int = expression2 match {
          case Lit(n) => n
          case Add(a, b) => a.asInstanceOf[Int] + b.asInstanceOf[Int]
          case Mult(a, b) => a.asInstanceOf[Int] * b.asInstanceOf[Int]
        }
      }
    

    打印解释器:

    def print: Expression2 ~> Id = new (Expression2 ~> Id) {
          override def apply[A](fa: Expression2[A]): Id[A] = eval(fa).asInstanceOf[A]
    
          def eval[A](expression2: Expression2[A]): String = expression2 match {
            case Lit(n) => n.toString
            case Add(a, b) => "(" + a.toString + " + " + b.toString + ")"
            case Mult(a, b) => "(" + a.toString + " * " + b.toString + ")"
          }
        }
    

    数学输入选项解释器:

    def evalOpt: Expression2 ~> Option = new (Expression2 ~> Option) {
        override def apply[A](fa: Expression2[A]): Option[A] = eval(fa).map{_.asInstanceOf[A]}
    
        def eval[A](expression2: Expression2[A]): Option[Int] = expression2 match {
          case Lit(n) => Option(n)
          case Add(a, b) => Option(a.asInstanceOf[Int] + b.asInstanceOf[Int])
          case Mult(a, b) => Option(a.asInstanceOf[Int] * b.asInstanceOf[Int])
        }
      }
    

    关于选项解释器,我本来希望a和b变为选项,而在字符串解释器a和b变为字符串,因为我的adt结果类型是a:expression2[a]。

    我也试着用lit[a](a:int)代替lit[a](a:a),但后来它坏了:当a在我的程序中被固定为int时,我不能为同一个表达式传递不同的解释器,我希望不必为不同的解释器重写我的程序。

    0 回复  |  直到 7 年前
        1
  •  2
  •   Dominic Egger    7 年前

    有几件事。一般来说你真的想避免 asInstanceOf 因为现在你可以 Expression2 对于任何类型,它都会在求值时崩溃,因为它实际上不是 Int . 有几种方法可以缓解这种情况。您可以只修复 表达2

    import scalaz._
    import Scalaz._
    
    
    trait Expression2[A] extends Product with Serializable
    case class Lit[A](a: Int) extends Expression2[Int]
    case class Add[A](a: Int, b: Int) extends Expression2[Int]
    case class Mult[A](a: Int, b: Int) extends Expression2[Int]
    
    type ExprAlg[A] = Free[Expression2, A]
    
    def lit(a: Int): ExprAlg[Int] = Free.liftF(Lit(a))
    def add(a: Int, b: Int): ExprAlg[Int] = Free.liftF(Add(a, b))
    def mult(a: Int, b: Int): ExprAlg[Int] = Free.liftF(Mult(a, b))
    
    val eval: Expression2 ~> Id = new (Expression2 ~> Id) {
        override def apply[A](fa: Expression2[A]): Id[A] = eval(fa)
    
        def eval[A](expression2: Expression2[A]): A = expression2 match {
          case Lit(n) => n
          case Add(a, b) => a+b
          case Mult(a, b) => a*b
        }
      }
    

    或者您可以将此功能与这样的操作关联起来。基本上,你可以把你的ADT中的案例 Add 例如:case类的参数类似于函数参数,您放入Extends中的类型是result类型。

    import scalaz._
    import Scalaz._
    import spire.algebra._
    import spire.implicits._ 
    
    
    trait Expression2[A] extends Product with Serializable
    case class Lit[A](a: A) extends Expression2[A]
    case class Add[A](a: A, b: A)(implicit val ev:Semiring[A]) extends Expression2[A]
    case class Mult[A](a: A, b: A)(implicit val ev:Semiring[A]) extends Expression2[A]
    
    type ExprAlg[A] = Free[Expression2, A]
    
    def lit[A](a: A): ExprAlg[A] = Free.liftF(Lit(a))
    def add[A](a: A, b: A)(implicit ev:Semiring[A]): ExprAlg[A] = Free.liftF(Add(a, b))
    def mult[A](a: A, b: A)(implicit ev:Semiring[A]): ExprAlg[A] = Free.liftF(Mult(a, b))
    
    
    val eval: Expression2 ~> Id = new (Expression2 ~> Id) {
      override def apply[A](fa: Expression2[A]): Id[A] = eval(fa)
    
      def eval[A](expression2: Expression2[A]): Id[A] = expression2 match {
        case Lit(n) =>  n
        case x:Add[A] => x.ev.plus(x.a,x.b)
        case x:Mult[A] => x.ev.times(x.a,x.b)
      }
    }  
    
    def program[A: Semiring](a:A,b:A) = for {
          two <- lit(a)
          four <- lit(b)
          sum <- add(two, four)
        } yield sum
    
    println(program[Int](2,4).foldMap(eval) )
    

    至于你的期权,我不太清楚你为什么要在这里解释为期权。如果你能做到 F ~> Id 对一些人来说 F 我是说, F ~> Option 真的只是 Some 应用于第一次自然转化。

    推荐文章