Scala: Function with dynamic contract as argument - scala

I have the following enum.
object Smth extends Enumeration {
class Value(name: String, func: (String, Int) => Boolean) extends super.Val(name)
private def opA(index: Int) : Boolean ={
index > 0
}
private def opB(x: String, index: Int) : Boolean ={
index > 0 && x.length > 0
}
val x = new Value("X", opA) // type mismatch error for opA
val y = new Value("Y", opB)
}
Enum constructor takes as an argument a function, of type (String, Int) => Boolean. Is it possible to create enum constructor in such a way that it will accept functions with 2 distinct contracts, for example:
(String, Int) => Boolean
(Int) => Boolean
I am trying to avoid using default value for argument, in function definition.
This is how I would like to use it.
if(Smth.x.func(0)) { do smth }
else if(Smth.y.func("str", 0)) { do smthElse }

If you just want default value you can do this:
class Value(val name: String, val func: (String, Int) => Boolean) extends super.Val(name) {
def apply(name: String, func: (Int) => Boolean): Value =
new Value(name, (x -> func("default value", x)))
}

Related

High order functions in Scala

IM trying to understand the below high order functions in Scala but need some clarifications on the parameters of the functions.
Questions:-
What does the Int => String in the apply function mean?
v: Int indicates that the parameter v is of type Int.
What does the [A](x: A) mean in layout function?
object Demo {
def main(args: Array[String]) {
println( apply( layout, 10) )
}
def apply(f: Int => String, v: Int) = f(v)
def layout[A](x: A) = "[" + x.toString() + "]"
}
f: Int => String means that f is a function with one argument of type Int and with a return type String
def layout[A](x: A) means that parameter x is of type A, which can be of any type. Here are a couple of examples on how to invoke layout:
layout[String]("abc") //returns "[abc]"
layout[Int](123) //returns "[123]"
When main runs it invokes apply with the layout function and the argument 10. This will output "[10]"
The syntax of Int => String means passing a function that accepts Int and returns String.
Here is a useful example for passing function:
case class Person(name: String, lastName: String)
val person = Person("Johnny", "Cage")
def updateName(name: String) = {
updatePerson(_.copy(name = name))
}
def updateLastName(lastName: String) {
updatePerson(_.copy(lastName = lastName))
}
private def updatePerson(transformer: Person => Person): Unit = {
transformer(person)
}
Note how each update function passes the copy constructor.

No implicit view available from AnyVal => org.scalacheck.Prop. [error] property

I have 2 questions
I am trying to learn scalacheck
Question 1)
Here is the test I am writing which is throwing the error. Can you please point to which page from docmentation i should read to understand reason behind this error.
case class Student(name:String, age:Int, mathsScore:Int, scienceScore:Int){
require(name != null ,"Name cannot be blank")
require(age > 3 ,"Age should be more than 3")
require(mathsScore >= 0 , "Score should not be negative")
require(scienceScore >= 0 ,"Score should not be negative")
val totalScore = mathsScore + scienceScore
}
Test is
object CaseStudySpecification extends Properties("Case Study Specification") {
property("nullName") = forAll { (name: String, age: Int, ms: Int, ss: Int) =>
if (name == null)
Prop.throws(classOf[IllegalArgumentException]) {
val x = Student(name, age, ms, ss)
}
}
}
Error is
No implicit view available from AnyVal => org.scalacheck.Prop.
[error] property("nullName") = forAll { (name: String, age: Int, ms: Int, ss: Int) =>
[error] ^
Question 2)
The official documentation gives one example test class as
property("stringLength") = Prop.forAll { s: String =>
val len = s.length
(s+s).length == len+len
}
I also read that it can be written as
val stringLength = Prop.forAll { s: String =>
val len = s.length
(s+s).length == len+len
}
How can i run the second form of test code , as when i run sbt test nothing happens for second version.
Both of the above snippets are in
object Ch3 extends Properties("String") {
}
The signature of Prop.forAll being called requires a function returning Prop (or at least something that can be implicitly converted to Prop) but, as written, the function:
(name: String, age: Int, ms: Int, ss: Int) => {
if (name != null) Prop.throws(...)
}
has an inferred signature of (String, Int, Int, Int) => AnyVal, and there does not exist an implicit conversion into a property. Hence the compilation error. The function could be trivially fixed by making sure it always returns a Boolean as follows:
property("nullName") = forAll { (name: String, age: Int, ms: Int, ss: Int) =>
if (name == null) Prop.throws(classOf[IllegalArgumentException]) {
Student(name, age, ms, ss)
}
else true
}
This results in the implicit Boolean => Prop function being applied, and the code compiles. A more idiomatic fix would be to rewrite the property using the implication operator:
property("nullName") = forAll { (name: String, age: Int, ms: Int, ss: Int) =>
name == null ==>
Prop.throws(classOf[IllegalArgumentException]) {
Student(name, age, ms, ss)
}
}
However it is not a good idea to reject too much of the generated input and since the very first value that scalacheck generates is in fact null, the property ends up as 'undecided' so the test still fails. You could just simplify your property to:
property("nullName") = forAll { (age: Int, ms: Int, ss: Int) =>
Prop.throws(classOf[IllegalArgumentException]) {
Student(null, age, ms, ss)
}
}
As this isn't a scalacheck-specific problem but rather a general Scala one it isn't specifically covered in the scalacheck documentation; you can read up on implicit views for more background.

Passing function to overloaded method in Scala

I've hit an interesting issue with passing function references to overloaded methods in Scala (Using 2.11.7)
The following code works as expected
def myFunc(a: Int, b: String): Double = {
a.toDouble + b.toDouble
}
def anotherFunc(value: String, func: (Int, String) => Double) = {
func(111, value)
}
anotherFunc("123123", myFunc)
But the following code doesn't compile
def myFunc(a: Int, b: String): Double = {
a.toDouble + b.toDouble
}
def anotherFunc(value: String, func: (Int, String) => Double) = {
func(111, value)
}
def anotherFunc(value: Int, func: (Int, String) => Double) = {
func(value, "123123")
}
anotherFunc("123123", myFunc)
Compiler shouts the following
scala> anotherFunc("123123", myFunc)
<console>:13: error: type mismatch;
found : String("123123")
required: Int
anotherFunc("123123", myFunc)
Are you using Scala REPL? One of it's design decisions is that if you have two variables/functions with the same name defined then "last defined wins". In your case it is a function with Int parameter.
You can print all defined symbols in REPL using:
$intp.definedTerms.foreach(println)
Here someone had similar question: Why its possible to declare variable with same name in the REPL?
I don't know the reason but seems you have to write
anotherFunc("123123", myFunc _)
to make it work.

How can I specify multiple constructors in the case class?

I'm trying to create a case class with multiple constructors:
object App {
def main(args: Array[String]) {
val a = Something("abc", 100500, _ % 2 == 0)
val b = Something(true, 10, 20)
println(s"$a - $b")
}
}
case class Something(s: String, n: Int, p: Int => Boolean) {
/*
Additional constructor -- Wrong way! -- it is imposible to invoke it outside the class
def this(b: Boolean, x: Int, y: Int) {
this("", 0, (i: Int) => i % x + y == 0)
}
*/
}
So far my code doesn't work:
Error:(10, 23) type mismatch;
found : Boolean(true)
required: String
val b = Something(true, 10, 20)
^
To fix it I need to create a companion object to hold an apply function which represents a new constructor for Something class:
object Something {
def apply(b: Boolean, x: Int, y: Int) = {
new Something(if (b) "" else "default", 0, _ => {
(x + y) % 2 == 0
})
}
}
It is inconvenient. Maybe there is some other way to place multiple constructors into the case class?
Actually it works, but you have to use new as auxiliary constructors do not have apply generated for case class:
case class Something(s: String, n: Int, p: Int => Boolean) {
def this(b: Boolean, x: Int, y: Int) {
this("", 0, (i: Int) => i % x + y == 0)
}
}
new Something(true, 5, 5) // Works
If you want Something(true, 5, 5) to work, you need to create companion object as you said. I think this is because otherwise case class won't be able to work with pattern matching as it is now, or it would have been much more complicated. And notice that pattern matching won't work in this case
Also remember that case class supports default constructors like case class Something(s: String = "default") this might help you, but it does not fix your example unfortunately

Retrieve typed stored values from Map

I'd like to put some data into a HashMap and retrieve these as typed values using a function. The function takes the expected type and also a default value in case the value is not stored in the HashMap. Type erasure of the JVM makes this a tricky thing.
Q: How can I retrieve a typed value?
Code and results below.
abstract class Parameters(val name: String) {
val parameters = new HashMap[String, Any]()
def put(key: String, value: Any) = parameters(key) = value
def get(key: String) = parameters.getOrElse(key, None)
def remove(key: String) = parameters.remove(key)
def g0[T: TypeTag](key: String, defaultValue: T) = {
get(key) match {
case x: T => x
case None => defaultValue
case _ => defaultValue
}
}
def g1[T: ClassTag](key: String, defaultValue: T) = {
val compareClass = implicitly[ClassTag[T]].runtimeClass
get(key) match {
case None => defaultValue
case x if compareClass.isInstance(x) => x.asInstanceOf[T]
}
}
}
class P extends Parameters("AParmList") {
put("1", 1)
put("3", "three")
put("4", 4.0)
put("width", 600)
println(g0[Int]("width", -1))
println(g0[Int]("fail", -2))
println(g1[Int]("width", -3))
println(g1[Int]("fail", -4))
}
object TypeMatching {
def main(args: Array[String]) {
new P
}
}
The output is (comments in parenthesis):
600 (as expected)
None (expected -2)
and a match error (java.lang.Integer stored, Int required)