case class Var(x: Int,y:Int)
def compare(v:Var,compare:String): Boolean = {
v.x compare v.y //--NeedHelp
}
def getComp(v:Var,compName:String):Int={
v.compName //--NeedHelp
}
val v = Var(2,3)
assert(true == compare(v,"<"))
assert(false == compare(v,"=="))
assert(false == compare(v,">"))
assert(2==getComp(v,"x"))
assert(3==getComp(v,"y"))
there are two //--NeedHelp statements, how can I go and write it so that it executes as expected. What I am trying to do, what it is called in Programming world? So that I can Google myself also and know more about it.
So, I think there are two questions here really:
Firstly, getting the code working:
For a very simple, but not very extensible or robust implementation, you can "pattern match" on the Strings to decide what to do:
def compare(v:Var,compare:String): Boolean = {
compare match {
case op if op == "<" => v.x < v.y
case op if op == ">" => v.x > v.y
case _ => false // not very robust, anything could be passed in.
}
}
Side Note: It is worth considering how to prevent the user from passing in unexpected values - for example, if the function took a trait of type Operator instead of just taking the String value, then the compiler would prevent invalid values from being passed in.
def getComp(v:Var,compName:String):Int={
compName match {
case name if name == "x" => v.x
case name if name == "y" => v.y
case _ => 0 // not very robust...
}
}
Both of the above statements could easily be implemented using an if statement either, e.g.
def getComp(v:Var,compName:String):Int={
if (compName == "x")
v.x
else if (compName == "y")
v.y
else
0 // again, not very robust.
}
A more advanced implementation of a similar pattern might make use of some of Scala's object oriented features to create different expression types and use parsers to parse a string of text, convert the text to expressions and then evaluate the expressions.
Secondly, in response to "..what is this called in the Programming World?.."
I think this covers a number of areas. It covers an interpreter, because a lot of what you are writing here is basically an interpreter, i.e. you are passing a string that represents a comparison operation and supplying data in the form to be evaluated in the context of that operation. It could also be referred to as a simple DSL (Domain Specific Language).
Also, just a personal recommendation but I believe, if I recall correctly, the Coursera Scala Course which was run by Martin Odersky, covered off a very simple Expression evaluation example in one of the videos (I think perhaps it is week 4). As I recall, it was a very simple one, but it shows at least at a high level how this kind of approach could be implemented in Scala.
In the general case, what you're looking for is reflection.
You can also find more information online on reflection in Scala. As far as I know Scala operators are methods, which would maybe let you use reflection to find them, but I haven't tried it myself.
Related
I understand that case class causes the compiler to augment a class with boilerplate to implement a certain useful pattern ("plain and immutable data-holding objects that should exclusively depend on their constructor arguments").
But the word "case" itself has no meaning to me in this context. I'm accustomed to its use as part of switch statements in C#, but that seems to have no relevance to Scala's use of the word.
I find it easier to program when I can attach words to particular meanings. Right now my mental model is case => boilerplate, in the same way that it could be blurg => boilerplate. It's a working mental model, but ambiguity makes it easy to misunderstand or to forget altogether.
So what does the word case have to do with what the compiler actually does?
I'm not looking for "what was in the mind of the designers" but rather "what's a rational way to relate this term to its meaning, given general knowledge of the language's design."
In my opinion, the term case comes from case analysis which is a reasoning technique enabled by special structures called algebraic data types. By itself case in case class might not make much sense, but when it forms a part of a sealed structure, which is how Scala defines ADTs, for example
sealed trait Number
case object Zero extends Number
case class Succ(v: Number) extends Number
then we see there are two forms of constructing Numbers, namely using Zero and Succ constructors. Hence whenever we have to think about Numbers, we at least know there are two different cases to consider. For example, say we want to define addition on Numbers, then its definition will have to handle two cases, perhaps, like so
def sum(a: Number, b: Number): Number =
a match {
case Zero => b
case Succ(v) => Succ(sum(v, b))
}
where
sum(Succ(Zero), Succ(Zero)) == Succ(Succ(Zero)) // 1 + 1 = 2
sum(Succ(Succ(Zero)), Succ(Zero)) == Succ(Succ(Succ(Zero))) // 2 + 1 = 3
sum(Succ(Zero), Succ(Succ(Zero))) == Succ(Succ(Succ(Zero))) // 1 + 2 = 3
sum(Zero, Succ(Succ(Zero))) == Succ(Succ(Zero)) // 0 + 2 = 2
sum(Succ(Succ(Zero)), Zero) == Succ(Succ(Zero)) // 2 + 0 = 2
Note how Scala in order to define ADT uses terms like class, object, trait etc., which appear to be from the object-oriented paradigm, however ADTs conceptually have little in common with class hierarchies found in OO. Personally I find this confusing, but we must remember Scala is both functional and OO language, which might be a reason for such terminological spillover. In some other more "pure" languages case class of ADT is represented simply by a vertical bar |, say like so
Inductive nat : Type :=
| O : nat
| S : nat -> nat.
My suggestion would be to try not to be a "slave to words" but instead words should serve you. What is important is the meaning behind the words or terms, not the words themselves. Do not build mental models around the terms, instead build mental models around the heavy concepts they are struggling to carry across feeble bridges of communication. In my opinion, the concept case is trying to communicate is that of ADT.
C# has a switch / case language feature which allows controlling program flow by matching an input to a set of possible values.
public static GetEmail(string name)
{
switch (name)
{
case "bill":
return "bill#example.com";
case "jane":
return "jane#example.com";
default:
return null;
}
}
Here, case roughly means "in the case that the given value is equal to this one, do X."
Scala's match / case feature is sort of like C#'s feature.
def getEmail(name: String): Option[String] = {
name match {
case "bill" => Option("bill#example.com")
case "jane" => Option("jane#example.com")
case _ => None
}
}
But it's much more powerful. It is designed to evaluate "matches" against things farm more complex than strings. To take advantage of this powerful matching feature, you define immutable data-holding classes with case class.
Here is a trivial, but hopefully helpful, example of a case class and its use with match / case:
case class Person(name: String, hasEmail: Boolean)
object EmailHelper {
def getEmail(person: Person): Option[String] = {
person match {
case Person(_, false) => None
case Person("bill", true) => Option("bill#example.com")
case Person("jane", true) => Option("jane#example.com")
case _ => None
}
}
}
In short, case class enforces a set of constraints which make a class usable with match / case.
I am a new to Scala coming from Java background, currently confused about the best practice considering Option[T].
I feel like using Option.map is just more functional and beautiful, but this is not a good argument to convince other people. Sometimes, isEmpty check feels more straight forward thus more readable. Is there any objective advantages, or is it just personal preference?
Example:
Variation 1:
someOption.map{ value =>
{
//some lines of code
}
} orElse(foo)
Variation 2:
if(someOption.isEmpty){
foo
} else{
val value = someOption.get
//some lines of code
}
I intentionally excluded the options to use fold or pattern matching. I am simply not pleased by the idea of treating Option as a collection right now, and using pattern matching for a simple isEmpty check is an abuse of pattern matching IMHO. But no matter why I dislike these options, I want to keep the scope of this question to be the above two variations as named in the title.
Is there any objective advantages, or is it just personal preference?
I think there's a thin line between objective advantages and personal preference. You cannot make one believe there is an absolute truth to either one.
The biggest advantage one gains from using the monadic nature of Scala constructs is composition. The ability to chain operations together without having to "worry" about the internal value is powerful, not only with Option[T], but also working with Future[T], Try[T], Either[A, B] and going back and forth between them (also see Monad Transformers).
Let's try and see how using predefined methods on Option[T] can help with control flow. For example, consider a case where you have an Option[Int] which you want to multiply only if it's greater than a value, otherwise return -1. In the imperative approach, we get:
val option: Option[Int] = generateOptionValue
var res: Int = if (option.isDefined) {
val value = option.get
if (value > 40) value * 2 else -1
} else -1
Using collections style method on Option, an equivalent would look like:
val result: Int = option
.filter(_ > 40)
.map(_ * 2)
.getOrElse(-1)
Let's now consider a case for composition. Let's say we have an operation which might throw an exception. Additionaly, this operation may or may not yield a value. If it returns a value, we want to query a database with that value, otherwise, return an empty string.
A look at the imperative approach with a try-catch block:
var result: String = _
try {
val maybeResult = dangerousMethod()
if (maybeResult.isDefined) {
result = queryDatabase(maybeResult.get)
} else result = ""
}
catch {
case NonFatal(e) => result = ""
}
Now let's consider using scala.util.Try along with an Option[String] and composing both together:
val result: String = Try(dangerousMethod())
.toOption
.flatten
.map(queryDatabase)
.getOrElse("")
I think this eventually boils down to which one can help you create clear control flow of your operations. Getting used to working with Option[T].map rather than Option[T].get will make your code safer.
To wrap up, I don't believe there's a single truth. I do believe that composition can lead to beautiful, readable, side effect deferring safe code and I'm all for it. I think the best way to show other people what you feel is by giving them examples as we just saw, and letting them feel for themselves the power they can leverage with these sets of tools.
using pattern matching for a simple isEmpty check is an abuse of pattern matching IMHO
If you do just want an isEmpty check, isEmpty/isDefined is perfectly fine. But in your case you also want to get the value. And using pattern matching for this is not abuse; it's precisely the basic use-case. Using get allows to very easily make errors like forgetting to check isDefined or making the wrong check:
if(someOption.isEmpty){
val value = someOption.get
//some lines of code
} else{
//some other lines
}
Hopefully testing would catch it, but there's no reason to settle for "hopefully".
Combinators (map and friends) are better than get for the same reason pattern matching is: they don't allow you to make this kind of mistake. Choosing between pattern matching and combinators is a different question. Generally combinators are preferred because they are more composable (as Yuval's answer explains). If you want to do something covered by a single combinator, I'd generally choose them; if you need a combination like map ... getOrElse, or a fold with multi-line branches, it depends on the specific case.
It seems similar to you in case of Option but just consider the case of Future. You will not be able to interact with the future's value after going out of Future monad.
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Promise
import scala.util.{Success, Try}
// create a promise which we will complete after sometime
val promise = Promise[String]();
// Now lets consider the future contained in this promise
val future = promise.future;
val byGet = if (!future.value.isEmpty) {
val valTry = future.value.get
valTry match {
case Success(v) => v + " :: Added"
case _ => "DEFAULT :: Added"
}
} else "DEFAULT :: Added"
val byMap = future.map(s => s + " :: Added")
// promise was completed now
promise.complete(Try("PROMISE"))
//Now lets print both values
println(byGet)
// DEFAULT :: Added
println(byMap)
// Success(PROMISE :: Added)
Take the following example, why is the extractor called multiple times as opposed to temporarily storing the results of the first call and matching against that. Wouldn't it be reasonable to assume that results from unapply would not change given the same string.
object Name {
val NameReg = """^(\w+)\s(?:(\w+)\s)?(\w+)$""".r
def unapply(fullName: String): Option[(String, String, String)] = {
val NameReg(fname, mname, lname) = fullName
Some((fname, if (mname == null) "" else mname, lname))
}
}
"John Smith Doe" match {
case Name("Jane", _, _) => println("I know you, Jane.")
case Name(f, "", _) => println(s"Hi ${f}")
case Name(f, m, _) => println(s"Howdy, ${f} ${m}.")
case _ => println("Don't know you")
}
Wouldn't it be reasonable to assume that results from unapply would not change given the same string.
Unfortunately, assuming isn't good enough for a (static) compiler. In order for memoizing to be a legal optimization, the compiler has to prove that the expression being memoized is pure and referentially transparent. However, in the general case, this is equivalent to solving the Halting Problem.
It would certainly be possible to write an optimization pass which tries to prove purity of certain expressions and memoizes them iff and only iff it succeeds, but that may be more trouble than it's worth. Such proofs get very hard very quickly, so they are only likely to succeed for very trivial expressions, which execute very quickly anyway.
What is a pattern match? The spec says it matches the "shape" of the value and binds vars to its "components."
In the realm of mutation, you have questions like, if I match on case class C(var v: V), does a case C(x) capture the mutable field? Well, the answer was no:
https://issues.scala-lang.org/browse/SI-5158
The spec says (sorry) that order of evaluation may be changed, so it recommends against side-effects:
In the interest of efficiency the evaluation of a pattern matching
expression may try patterns in some other order than textual sequence.
This might affect evaluation through side effects in guards.
That's in relation to guard expressions, presumably because extractors were added after case classes.
There's no special promise to evaluate extractors exactly once. (Explicitly in the spec, that is.)
The semantics are only that "patterns are tried in sequence".
Also, your example for regexes can be simplified, since a regex will not be re-evaluated when unapplied to its own Match. See the example in the doc. That is,
Name.NameReg findFirstMatchIn s map {
case Name("Jane",_,_) =>
}
where
object Name { def unapply(m: Regex.Matcher) = ... }
Perhaps this is just my background in more imperative programming, but I like having return statements in my code.
I understand that in Scala, returns are not necessary in many methods, because whatever the last computed value is is returned by default. I understand that this makes perfect sense for a "one-liner", e.g.
def square(x) = x * x
I also understand the definitive case for using explicit returns (when you have several branches your code could take, and you want to break out of the method for different branches, e.g. if an error occurs). But what about multiline functions? Wouldn't it be more readable and make more sense if there was an explicit return, e.g.
def average(x: List[Int]) : Float = {
var sum = 0
x.foreach(sum += _)
return sum / x.length.toFloat
}
def average(x: List[Int]) : Float =
x.foldLeft(0)(_ + _) / x.length.toFloat
UPDATE: While I was aiming to show how an iterative code can be made into a functional expression, #soc rightly commented that an even shorter version is x.sum / x.length.toFloat
I usually find that in Scala I have less need to "return in the middle". Furthermore, a large function is broken into smaller expressions that are clearer to reason. So instead of
var x = ...
if (some condition) x = ...
else x = ...
I would write
if (some condition) ...
else ....
Similar thing happens using match expressions. And you can always have helper nested classes.
Once you are comfortable with many forms of expressions that evaluate to a result (e.g., 'if') without a return statement, then having one in your methods looks out of place.
At one place of work we had a rule of not having 'return' in the middle of a method since it is easy for a reader of your code to miss it. If 'return' is only at the last line, what's the point of having it at all?
The return doesn't tell you anything extra, so I find it actually clutters my understanding of what goes on. Of course it returns; there are no other statements!
And it's also a good idea to get away from the habit of using return because it's really unclear where you are returning to when you're using heavily functional code:
def manyFutures = xs.map(x => Futures.future { if (x<5) return Nil else x :: Nil })
Where should the return leave execution? Outside of manyFutures? Just the inner block?
So although you can use explicit returns (at least if you annotate the return type explicitly), I suggest that you try to get used to the last-statement-is-the-return-value custom instead.
Your sense that the version with the return is more readable is probably a sign that you are still thinking imperatively rather than declaratively. Try to think of your function from the perspective of what it is (i.e. how it is defined), rather than what it does (i.e. how it is computed).
Let's look at your average example, and pretend that the standard library doesn't have the features that make an elegant one-liner possible. Now in English, we would say that the average of a list of numbers is the sum of the numbers in the list divided by its length.
def average(xs: List[Int]): Float = {
var sum = 0
xs.foreach(sum += _)
sum / xs.length.toFloat
}
This description is fairly close to the English version, ignoring the first two lines of the function. We can factor out the sum function to get a more declarative description:
def average(xs: List[Int]): Float = {
def sum(xs: List[Int]): Int = // ...
sum(xs) / xs.length.toFloat
}
In the declarative definition, a return statement reads quite unnaturally because it explicitly puts the focus on doing something.
I don't miss the return-statement at the end of a method because it is unnecessary. Furthermore, in Scala each method does return a value - even methods which should not return something. Instead of void there is the type Unit which is returned:
def x { println("hello") }
can be written as:
def x { println("hello"); return Unit }
But println returns already the type Unit - thus when you explicitly want to use a return-statement you have to use it in methods which return Unit, too. Otherwise you do not have continuous identical build-on code.
But in Scala there is the possibility to break your code in a lot of small methods:
def x(...): ReturnType = {
def func1 = ... // use funcX
def func2 = ... // use funcX
def func3 = ... // use funcX
funcX
}
An explicitly used return-statement does not help to understand the code better.
Also Scala has a powerful core library which allows you to solve many problems with less code:
def average(x: List[Int]): Float = x.sum.toFloat / x.length
I believe you should not use return, I think it somehow goes against the elegant concept that in Scala everything is an expression that evaluates to something.
Like your average method: it's just an expression that evaluates to Float, no need to return anything to make that work.
Scala has a language feature to support disjunctions in pattern matching ('Pattern Alternatives'):
x match {
case _: String | _: Int =>
case _ =>
}
However, I often need to trigger an action if the scrutiny satisfies PatternA and PatternB (conjunction.)
I created a pattern combinator '&&' that adds this capability. Three little lines that remind me why I love Scala!
// Splitter to apply two pattern matches on the same scrutiny.
object && {
def unapply[A](a: A) = Some((a, a))
}
// Extractor object matching first character.
object StartsWith {
def unapply(s: String) = s.headOption
}
// Extractor object matching last character.
object EndsWith {
def unapply(s: String) = s.reverse.headOption
}
// Extractor object matching length.
object Length {
def unapply(s: String) = Some(s.length)
}
"foo" match {
case StartsWith('f') && EndsWith('f') => "f.*f"
case StartsWith('f') && EndsWith(e) && Length(3) if "aeiou".contains(e) => "f..[aeiou]"
case _ => "_"
}
Points for discussion
Is there an existing way to do this?
Are there problems with this approach?
Can this approach create any other useful combinators? (for example, Not)
Should such a combinator be added to the standard library?
UPDATE
I've just been asked how the compiler interprets case A && B && C. These are infix operator patterns (Section 8.1.9 of the Scala Reference). You could also express this with standard extract patterns (8.1.7) as &&(&&(A, B), C).' Notice how the expressions are associated left to right, as per normal infix operator method calls likeBoolean#&&inval b = true && false && true`.
I really like this trick. I do not know of any existing way to do this, and I don't foresee any problem with it -- which doesn't mean much, though. I can't think of any way to create a Not.
As for adding it to the standard library... perhaps. But I think it's a bit hard. On the other hand, how about talking Scalaz people into including it? It looks much more like their own bailiwick.
A possible problem with this is the bloated translation that the pattern matcher generates.
Here is the translation of the sample program, generated with scalac -print. Even -optimise fails to simplify the if (true) "_" else throw new MatchError() expressions.
Large pattern matches already generate more bytecode than is legal for a single method, and use of this combinator may amplify that problem.
If && was built into the language, perhaps the translation could be smarter. Alternatively, small improvements to -optimise could help.