noncom
noncom

Reputation: 4992

Scala - designation of a matched item in pattern matching

I have the following code:

class Animal(hair: Option[Hair])

class Cat(var hair: Option[Hair]) extends Animal(hair)
class Dog(var hair: Option[Hair]) extends Animal(hair)
class Sheep(var hair: Option[Hair]) extends Animal(hair)

//then somewhere else:

def what(animal: Animal) {

  animal match {
    case Cat(hair) => println("processing cat, hair=" + hair)
    case Dog(hair) => println("processing dog, hair=" + hair)
    case Sheep(hair) => {
      println("processing sheep, cutting hair...")
      hair = None
    }
  }
}

The questions are:

1) When pattern matching succeeds with a Sheep, how can I access it's hair and change it? It complained about reassignment to val, and I have then placed var in the constructor but still...

2) One more way I can think of is to assign the entire matched value to a variable, is there any way to bind a value matched by some case class constructor pattern to a variable?

(I know that I could probably pattern match on something like s: Sheep and then call s.changeHairTo(None) but that is the least preferrable way).

Upvotes: 8

Views: 3374

Answers (2)

4e6
4e6

Reputation: 10776

You can use @ to bind the whole pattern to variable in your version

class Animal(hair: Option[Hair])
case class Cat(var hair: Option[Hair]) extends Animal(hair)
case class Dog(var hair: Option[Hair]) extends Animal(hair)
case class Sheep(var hair: Option[Hair]) extends Animal(hair)

def what(animal: Animal) {
  animal match {
    case Cat(hair) => println("processing cat, hair=" + hair)
    case Dog(hair) => println("processing dog, hair=" + hair)
    case s @ Sheep(hair) => {
      println("processing sheep, cutting hair...")
      //cut(hair)
      s.hair = None
    }
  }
}

But you don't have to use var. Here is more functional version of your snippet. what here just returns Sheep with None Hair after cutting.

trait Animal
case class Cat(hair: Option[Hair]) extends Animal
case class Dog(hair: Option[Hair]) extends Animal
case class Sheep(hair: Option[Hair]) extends Animal

def what(animal: Animal): Animal =
  animal match {
    case Cat(hair) => {
      println("processing cat, hair=" + hair)
      animal
    }
    case Dog(hair) => {
      println("processing dog, hair=" + hair)
      animal
    }
    case Sheep(hair) => {
      println("processing sheep, cutting hair...")
      //cut(hair)
      Sheep(None)
    }
  }
}

Upvotes: 25

drexin
drexin

Reputation: 24413

this does not work, because in the pattern matching the var "hair" is just extracted from the Sheep object so it is not the field of Sheep, but a variable in the context of the case block. You could do it like this:

class Hair

trait Animal {
  var hair: Option[Hair]
}
case class Cat(var hair: Option[Hair]) extends Animal
case class Dog(var hair: Option[Hair]) extends Animal
case class Sheep(var hair: Option[Hair]) extends Animal

//then somewhere else:

def what(animal: Animal) {

  animal match {
    case Cat(hair) => println("processing cat, hair=" + hair)
    case Dog(hair) => println("processing dog, hair=" + hair)
    case Sheep(hair) => {
      println("processing sheep, cutting hair...")
      animal.hair = None
    }
  }
}

Just tell Animal it has a mutable field hair and you can set it without casting it to the right type.

Upvotes: 4

Related Questions