1

I am trying to figure out which would be the most functional style for this situation

I have a Image model

case class Image(
  id: Int,
  name: String,
  title: String,
  permalink: String,
  url: String 
)

I have a TestHelper object that helps me when I write tests, because it allows me to create random images objects

package utils

import models.Pet
import scala.util.Random

object TestHelper {

  val random = new Random()

  def randomId = random.nextInt(Integer.MAX_VALUE)

  val nameList: List[String] = List("Joycelyn", "Shaunte", "Aurelio", "Jeane", "Carline", "Major", "Shawanna", "Hayden", "Benjamin", "Roxy", "Ardelia", "Yanira", "Tilda", "Claude", "Jonah", "Ilse", "Kieth", "Elmira", "Reid", "Bethann", "Catherine", "Yasuko", "Kia", "Merri", "Ethelyn", "Mallory", "Eustolia", "Matt", "Lynelle", "Christi", "Alane", "Miles", "Ressie", "Darryl", "Kathy", "Hiedi", "Kacy", "Cecila", "Tamar", "Dwayne", "Charlette", "Wanetta", "Sonja", "Celine", "Vina", "Teresa", "Dagny", "Debera", "Doreatha", "Wilda")

  def randomImage: Image = {
    var id = randomId
    var name = nameList(random.nextInt(nameList.length))
    var title = name
    var permalink = name.toLowerCase
    var logoUrl = s"https://www.images.com/${permalink}"
    Image(id, name, title, permalink, logoUrl)
  }

}

But I know that if I want to write in a functional style I should avoid using var. If I wouldn't use the field name, several times, it would be enough to replace all the vars with defs, but since I need to repeat the value, I am not sure how to write this in a functional style

4
  • 3
    Huh? If you don't need any vars, then replace var by val? What's the problem? Commented Apr 26, 2018 at 15:20
  • @AndreyTyukin thank you for the comment, I realized I did not made my point clear. The problem is, if I replace vars with vals, that I am always obtaining the same object. Commented Apr 26, 2018 at 15:26
  • What?... That sounds absurd. If you replace vars by vals, this will change nothing in the behavior of the code, because you are never mutating those vars anyway. Commented Apr 26, 2018 at 15:29
  • @AndreyTyukin you are right! Thank you for the comments, they are helping me to realize that some misconceptions I have. Commented Apr 26, 2018 at 15:47

3 Answers 3

3

Take a look at one of our libs(shameless disclaimer).

util-samplers

https://github.com/outworkers/util/blob/develop/util-samplers

It uses macros to navigate the structure of your case classes and generate appropriate samples. It's not a magic bullet but it will deal with most things most of the time, and it will also generate meaningful data wherever possible.

E.g if the field is called name, you will get a "Peter Smith" style result. It's also fully compatible with Scalacheck, but overall pretty basic, with a very simple macro. It's simplicity is guaranteed by having had me write it.

val imageGenerator = Sample.generator[Image]
implicit val imageArb = Sample.arbitrary[Image]

And you can plug that implicit in straight to your functional checkers.

forAll { img: Image => ....
}

If you don't want scalacheck at all, just use the basics:

import com.outworkers.util.samplers._

class MyTest extends FlatSpec {
  it should "upload an image to S3" in {
    val image = gen[Image]
    val images = genList[Image](25)

  }
}

If you cannot generate a type or the macro complains, simply write a sampler yourself. In most instances, you'd have something like a trait or object to hold all of them.

object ExtraSamples {
  implicit val cantAutomateThis: Sample[java.net.Bla] = new Sample[java.net.Bla] {
    override def sample: java.net.Bla = // in here you fill it in manuall....
  }
}

Then if you have a case class with a java.net.Bla field, you simply import ExtraSamples._ in places where you do gen, and your manual implementation will be used to construct more complex ones. That's how you can support anything not supported out of the box.

scalacheck-shapeless

This is a different take on the same problem but instead of macros it uses automated typeclass instance derivation capabilities from shapeless. It's not wildly different in its approach from util-samplers, but the code might be slightly more complex, yet higher level.

https://github.com/alexarchambault/scalacheck-shapeless

import org.scalacheck.ScalacheckShapeless._

//  If you defined:

// case class Foo(i: Int, s: String, blah: Boolean)
// case class Bar(foo: Foo, other: String)

// sealed trait Base
// case class BaseIntString(i: Int, s: String) extends Base
// case class BaseDoubleBoolean(d: Double, b: Boolean) extends Base

//  then you can now do

implicitly[Arbitrary[Foo]]
implicitly[Arbitrary[Bar]]
implicitly[Arbitrary[Base]]

I've never done a side to side comparison, and they are not intended to compete with each other. The first one is extremely fast and lightweight and has minimal overhead as it's just one macro, the shapeless one is more involved and comes with much higher compilation times but it's likely more advanced in terms of what types it can auto-generate.

Sign up to request clarification or add additional context in comments.

Comments

2

You can use ScalaCheck for this. ScalaCheck is a port of the functional language Haskell's library QuickCheck, which allows you to write random test example generators in a functional style.

Comments

1

In this particular case, you can simply replace all local vars by vals, because you are not mutating vars anyway.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.