Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala apply with no parameters

I want to create a program that generates random things when requested, such as letters in the below example. I've defined an abstract class with a companion object to return one of the subclasses.

  abstract class Letter

  class LetterA extends Letter {
    override def toString = "A"
  }
  class LetterB extends Letter {
    override def toString = "B"
  }
  class LetterC extends Letter {
    override def toString = "C"
  }

  object Letter {
    def apply = RNG.d(3) match {
      case 1 => new LetterA
      case 2 => new LetterB
      case 3 => new LetterC
    }
    override def toString = "Random Letter"
  }

The dream is that I'll be able to use Letter to get a random letter of type A, B or C. But with this code it just gives me the letter object. Letter.apply gives me A, B or C but is ugly.

If I change the apply function to be apply() then things are a little better as I can then use Letter() to get one of my random letters, but Letter still gives me the object.

Is there anything I can do so that assigning something to Letter actually resolves to one of LetterA, LetterB or LetterC?

like image 421
Goldstein Avatar asked Mar 16 '23 22:03

Goldstein


2 Answers

Answer to your question is no, you can't distinguish between Letter.type and Letter.apply, so you need a workaround.

I would suggest you to use Type-Class pattern for it, it's much more extenible than adding random-generating apply method to companion object

  trait RandomGenerator[T] {
    def gen: T        
  } 

  implicit object RandomLetter extends RandomGenerator[Letter] {

    private val rnd = new Random()

    def gen = (rnd.nextInt(3) + 1) match {
      case 1 => new LetterA
      case 2 => new LetterB
      case 3 => new LetterC
    }
  }

  def random[T: RandomGenerator]: T = 
    implicitly[RandomGenerator[T]].gen


  println(random[Letter])
  println(random[Letter])
  println(random[Letter])
like image 164
Eugene Zhulenev Avatar answered Mar 21 '23 12:03

Eugene Zhulenev


You could provide an implicit conversion from Letter.type to Letter in the companion object:

implicit def asLetter(companion: Letter.type): Letter = companion.apply

Then you can use Letter to get an object that can be implicitly converted to the Letter class. For example:

val x: Letter = Letter
val letters: Seq[Letter] = Seq.fill(10)(Letter)

But remember that this way you'll always have to ascribe the object to the class in use.

Note: I don't think this is a particularly good idea, but I just wanted to show that it is possible!

like image 36
Ben Reich Avatar answered Mar 21 '23 12:03

Ben Reich