简体   繁体   English

相当于Haskell模式匹配的Scala构造函数

[英]Scala equivalent to Haskell pattern Matching on constructors

In Haskell I have got constructors defined like this : 在Haskell中,我定义了如下构造函数:

data Fonction =
Const Float
    | Param String
    | Var String
    | Add Fonction Fonction

infix 3 @+

(@+) :: Fonction -> Fonction -> Fonction
(@+) (Const k) (Const k') = Const (k + k')

In Scala, quite new for me, I tried : 在Scala中,对我来说很新,我尝试过:

trait Function {
  case class Const(value: Double) extends Function
  case class Param(value: String) extends Function
  case class Var(value: String) extends Function
  case class Add(f1: Function, f2: Function) extends Function
}

object Op extends Function {
   def som(f1:Function, f2: Function): Function  = 
    (f1, f2) match {
       case (Const(a),Const(b)) => Const(a + b)
       case _  => Add(f1,f2)
   }  
}

object HelloWorld extends Function {
  def main(args: Array[String]): Unit = {
    val s =  Op.som(Const(3),Const(5))
    println(s)
   }
}

but the pattern (Const(3), Const(5)) isn't matched since I get Add(Const(3.0),Const(5.0)) 但是模式(Const(3),Const(5))不匹配,因为我得到了Add(Const(3.0),Const(5.0))

Although @nicodp code is technically correct (but IMHO not quite), it provides no explanation. 尽管@nicodp代码在技术上是正确的(但恕我直言不太正确),但没有提供任何解释。 The reason why your code doesn't work as expect is because you define your cases inside trait Function . 您的代码无法按预期工作的原因是因为您在trait Function定义了案例。 The thing is that in the Java world inner classes can be static and non-static and Scala copies this part of the behavior. 事实是,在Java世界中, 内部类可以是静态的,也可以是非静态的,并且Scala复制了行为的这一部分。 And classes defined inside a trait are non-static. trait中定义的类是非静态的。 Particularly it means that an instance of HelloWorld.Const(42) is not equal to an instance of Op.Const(42) because they capture different "outer" values of different types (the Op object and the HelloWorld object). 特别是,这意味着HelloWorld.Const(42)的实例不等于Op.Const(42)的实例,因为它们捕获不同类型( Op对象和HelloWorld对象)的不同“外部”值。 And this is also why your pattern matching doesn't work as you might expect. 这也是为什么模式匹配无法按预期工作的原因。 If you make you write your pattern matching as: 如果您将模式匹配写为:

object Op extends Function {
  def som(f1: Function, f2: Function): Function =
    (f1, f2) match {
      // this doesn't work
      //case (Const(a), Const(b)) => Const(a + b) 
      // this works
      case (HelloWorld.Const(a), HelloWorld.Const(b)) => Const(a + b)
      case _ => Add(f1, f2)
    }
}

it will work but this is probably not what you want. 它会起作用,但这可能不是您想要的。 The way to work this around is to define your case class es in a static context. 解决此问题的方法是在静态上下文中定义case class You may do it in just top level as in @nicodp suggested. 您可以按照建议的@nicodp在顶级级别上进行操作。 Another choice is to put them into a Function a companion object like this: 另一个选择是将它们放置在Function作为一个伴随对象,如下所示:

sealed trait Function 

object Function {
  case class Const(value: Double) extends Function
  case class Param(value: String) extends Function
  case class Var(value: String) extends Function
  case class Add(f1: Function, f2: Function) extends Function
}

object Op {

  import Function._

  def som(f1: Function, f2: Function): Function =
    (f1, f2) match {
      //          case (HelloWorld.Const(a), HelloWorld.Const(b)) => Const(a + b)
      case (Const(a), Const(b)) => Const(a + b)
      case _ => Add(f1, f2)
    }
}

object HelloWorld {
  import Function._

  def main(args: Array[String]): Unit = {
    val s = Op.som(Const(3), Const(5))
    println(s)
  }
}

Note that you replace extends Function with import Function._ 请注意,您更换extends Functionimport Function._

Also note that it is suggested to use sealed on such traits so you (compiler) know your pattern-matching is exhaustive. 另请注意,建议对此类特征使用sealed ,以便您(编译器)知道您的模式匹配是详尽的。

The types you have created are path dependent . 您创建的类型取决于路径 Change the type declaration to this: 将类型声明更改为此:

trait Function

object Function {
  case class Const(value: Double) extends Function
  case class Param(value: String) extends Function
  case class Var(value: String) extends Function
  case class Add(f1: Function, f2: Function) extends Function
}

import Function._

With your original defintion the Const need to be created inside of the same Function object to match. 使用原始定义,需要在相同的Function对象内部创建Const以进行匹配。 You are matching inside of Op , but the Const you are matching are created inside of HelloWorld , therefore you are matching HelloWorld.Const againt Op.Const . 您在Op内进行匹配,但是要在HelloWorld内创建要匹配的Const ,因此,您Op.Const内匹配HelloWorld.Const

Is there any reason why Op and HelloWorld are derived from the Function trait? 为什么OpHelloWorld源自Function特性有什么原因吗? (You do not use this in your example, but perhaps you have some reason out of that). (您在示例中没有使用它,但是也许您出于某些原因)。 Following would look more natural to me: 对我而言,以下内容看起来更自然:

object HelloWorld {
  def som(f1:Function, f2: Function): Function  = {
    (f1, f2) match {
      case (Const(a), Const(b)) => Const(a + b)
      case _ => Add(f1, f2)
    }
  }

  def main(args: Array[String]): Unit = {
    val s =  som(Const(3),Const(5))
    println(s)
  }
}

To define this piece of Haskell in Scala : Scala定义Haskell这一部分:

data Fonction =
Const Float
    | Param String
    | Var String
    | Add Fonction Fonction

I'd proceed as follows: 我将进行如下操作:

trait Function

case class Const(v: Float) extends Function
case class Param(v: String) extends Function
case class Var(v: String) extends Function
case class Add(f1: Function, f2: Function) extends Function

And now your code will work as expected: 现在您的代码将按预期工作:

scala> HelloWorld.main(Array())
Const(8.0)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM