Scala 案例类与逻辑惯用的方法是什么

Scala 案例类与逻辑惯用的方法是什么,scala,functional-programming,Scala,Functional Programming,FP的惯用方式是什么:假设我有这个 trait Name object Name{ def apply(name: String): Name = { if (name.trim.isEmpty || name.trim.length < 3) InvalidName else ValidName(name.trim) } } case object InvalidName extends Name case class V

FP的惯用方式是什么:假设我有这个

trait Name

object Name{
 def apply(name: String): Name = {
     if (name.trim.isEmpty || name.trim.length < 3)
       InvalidName
     else 
       ValidName(name.trim)
    }
 }

case object InvalidName extends Name
case class ValidName(name:String) extends AnyVal with Name
哪种方式更惯用:

case class Name private (name: String) {
  lazy val first :: last :: Nil = name.split(" ").toList
}
object Name {
  def fromString (name: String): Either[String, Name] = {
    if (name.trim.isEmpty || name.trim.length < 3) Left("Invalid name")
    else Right(new Name(name.trim))
  }
}
  • 将它们放在case类中,但这是如何使case类包含一些逻辑的,但是我可以做如下操作:

    val n=ValidName(“约翰·史密斯”)

    val(第一个,最后一个)=n.split

  • 将它们放在专用对象中,但方法如下所示

    def split(n:ValidName)=n.name.splitAt(“”)

  • 使用隐式类创建一个对象,该类将接受名称并调用方法


  • 您认为呢?

    案例类添加逻辑没有什么大问题,尤其是当它只是以不同的格式提取数据时。(将数据成员添加到
    案例类时会出现问题)

    所以我会做选择1,不用担心


    作为对这些评论的回应,
    case class
    实际上只是一个快捷方式,用于创建一个包含大量有用的预实现方法的类。特别是,
    unapply
    方法允许在模式匹配中使用
    case类
    ,并且
    equals
    对两个实例的字段进行元素级比较

    还有许多其他方法可以以不同的方式从
    案例类
    中提取数据。最明显的是
    toString
    copy
    ,但也有其他类似的
    hashCode
    以及从
    Product
    继承的所有内容,例如
    productIterator


    由于
    case类
    已经有了以有用的方式提取数据的方法,我认为添加
    split
    方法作为从
    case类
    提取数据的另一种方法是没有异议的,我认为这取决于上下文。在这种情况下,如果您使用的大多数方法只是轻微修改到<代码> String 方法,您可能需要考虑第四个选项:

    case class Name(name: String)
    implicit def NameToString(n: Name) = n.name
    
    Name("Iron Man").split(" ") // Array(Iron, Man)
    
    更地道的说法是:

    case class Name private (name: String) {
      lazy val first :: last :: Nil = name.split(" ").toList
    }
    object Name {
      def fromString (name: String): Either[String, Name] = {
        if (name.trim.isEmpty || name.trim.length < 3) Left("Invalid name")
        else Right(new Name(name.trim))
      }
    }
    
    case类名称private(名称:String){
    lazy val first::last::Nil=name.split(“”).toList
    }
    对象名{
    def fromString(名称:String):或者[String,name]={
    如果(name.trim.isEmpty | | name.trim.length<3)左侧(“无效名称”)
    else Right(新名称(Name.trim))
    }
    }
    
    或者可能是这样:

    case class Name (first: String, last: String) {
      lazy val fullName = s"$first $last"
    }
    object Name {
      def fromString (name: String): Either[String, Name] = {
        if (name.trim.isEmpty || name.trim.length < 3) Left("Invalid name")
        else {
          val first :: last :: Nil = name.split(" ").toList
          Right(new Name(first, last))
        }
      }
    }
    
    案例类名称(第一个:字符串,最后一个:字符串){
    lazy val fullName=s“$first$last”
    }
    对象名{
    def fromString(名称:String):或者[String,name]={
    如果(name.trim.isEmpty | | name.trim.length<3)左侧(“无效名称”)
    否则{
    val first::last::Nil=name.split(“”).toList
    右(新名称(第一个、最后一个))
    }
    }
    }
    
    在scala中,通过使用
    来表示失败案例比通过继承来表示更为惯用。如果您有一个
    N
    的实例,则无法对其调用任何函数,您可能必须对其进行模式匹配。但是像
    或者
    这样的类型已经有了
    映射
    折叠
    等功能,这使得它更容易使用

    拥有私有构造函数有助于确保只能创建有效的
    名称
    ,因为创建名称的唯一方法是通过
    fromString
    方法


    不要为此使用隐式。这是没有必要的,只会使代码混乱。实际上,隐式函数并不是用来做什么的。

    Case类不应该为用于定义代数结构和主要用于模式匹配的数据保存函数。实际上,这些预先实现的方法都是由trait product继承的,它没有这些。这就是我所说的,数据上的函数应该保持在其他实体的外部,如特征或对象等。谢谢Tim,我不知怎么地介于你的建议和我的观点之间,这与@RamanMishra所说的关于案例类是代数数据类型的观点一致。我通常倾向于将业务逻辑放在案例类之外。我当然可以创建一个包含此逻辑的对象作为选项3,但它似乎是冗余开销wdyt?@igx我同意业务逻辑不应该在
    案例类
    中,但我认为简单的操作是可以的,只要它们直接与类本身中的值相关。ADT可以使用其他方法来取消选中它们,以及模式匹配。请注意,在对象的
    apply
    方法中使用
    Name(…)
    将调用
    Name。apply(…)
    (调用自身)并溢出堆栈(我想,还没有测试过它)。。。您可能应该将其替换为
    新名称(…)
    ,以确保它调用类构造函数directly@AlvaroCarrasco很好,我过度简化了这个例子。正如你所说,我编辑了这个问题以避免出现这种情况,但模式匹配是一种特定的解决方案。这里的问题是,我们在Classis的一个成员上应用了一些商业逻辑,我确实看到了您的建议的好处,但是对于设计决策:您不想用隐式填充代码。您希望使用implicits(IMHO)来扩展外部lib以适合您的dsl或方便。或进行必要的转换。在您的示例中,您只需将
    名称
    转换为
    字符串
    ,这在某种程度上甚至是危险的。例如,您可以执行诸如``val Name=Name(“John Doe”)Name+“something”``之类的操作,这是意外的行为