In the course on Scala at Coursera (lecture 1.4, around 3 mins), Martin Odersky says that the expression true && e always returns e (e is any object). And the expression false || e also returns e. He explains that sometimes the last expression is not always evaluated.
But when I run these expressions I get error: type mismatch.
For true && 5 I get found: Int(5); required: Boolean
Has Scala evolved in recent times or what am I doing wrong?
e stands for boolean expression.
Predicate: a boolean expression to be evaluated e.g. (x >= 4), (x != 0), etc
see https://sites.google.com/a/stonybrook.edu/functional-programming-scala/lecture-1-4
From the Scala Reference book, paragraph 6.16 Conditional expressions, given:
if (e1) e2 else e3
The condition e1 is expected to conform to type Boolean. The then-part
e2 and the else-part e3 are both expected to conform to the expected
type of the conditional expression. The type of the conditional
expression is the weak least upper bound (§3.5.3) of the types of e2
and e3.
Related
I get a compiler error if I try this
df.filter($"foo" == lit(0))
forgetting that I need a triple equals in Spark.
However, if I do this, I get the wrong answer but no error:
df.filter($"foo".between(baz, quux) || $"foo" == lit(0))
Can someone explain why compile-time checks help me in the first case, but not the second?
Because $"foo" == lit(0) is always evaluated as Boolean = false.
So in the first case, you trying to call method filter by passing a Boolean whereas it expects a string expression or column expression. Thus you get an error.
Now in the second, case:
$"foo".between(baz, quux) || $"foo" == lit(0) is evaluated as:
(((foo >= baz) AND (foo <= quux)) OR false)
which is accepted beacause you doing an OR || between a column expression ($"foo".between(baz, quux)) and a literal boolean false.
In other words, it is interpreted as $"foo".between(baz, quux) || lit(false)
if statment in scala is behaving strangely
scala> val a = 10
a: Int = 10
scala> if (a > 10) 1
res10: AnyVal = ()
scala> if (a <= 10) 1
res12: AnyVal = 1
Why didn't we get return type as Int? Why we got AnyVal
if statment in scala is behaving strangely
Scala doesn't have an if statement. In fact, Scala has no statements at all. Scala has an if expression, or more precisely, a conditional expression.
In fact, if Scala had an if statement, then your question wouldn't make sense, because statements have no value and thus no type.
Why didn't we get return type as Int? Why we got AnyVal
As the documentation says:
The conditional expression if (e1) e2 else e3 chooses one of the values of e2 and e3, depending on the value of e1. […] The type of the conditional expression is the weak least upper bound of the types of e2 and e3.
This makes sense: the conditional can be either true or false, so the value of the conditional expression is either the "then" part or the else part. Since the value can be either the "then" part or the else part, the type of the expression obviously must be compatible with both.
In your case, the value of the "then" part is 1 whose type is Int and the value of the else part is () whose type is Unit:
A short form of the conditional expression eliminates the else-part. The conditional expression if (e1) e2 is evaluated as if it was if (e1) e2 else ().
Therefore, whatever the type of the whole expression is, it must be compatible with both Int and Unit. And the most-precise possible type that is compatible with both Int and Unit is AnyVal:[Source: Scala 2.13 Language Specification – Section 12 The Scala Standard Library]
As you can see in the left half of the tree, the weak-LUB of Int and Unit is obviously AnyVal.
Using a pattern variable that names an entire case expression makes the difference between compiling correctly and the error "An infinite type was inferred for an expression". The following compiles correctly:
m = case Left "anything" of
e#(Left err) -> Left err
(Right f) -> case lookup "key" f of
Nothing -> Left "something else"
(Just x) -> Right x
However, if we use the pattern variable e (substituting e for "Left err", functionally equivalent), the compiler flags an error:
m = case Left "anything" of
e#(Left err) -> e
(Right f) -> case lookup "key" f of
Nothing -> Left "something else"
(Just x) -> Right x
"An infinite type was inferred for an expression: StrMap t1 while trying to match type t1 with type StrMap t1 while checking that expression case ((lookup "key") f) ...
I understand that matching StrMap t1 with t1 is problematic. I do not understand why this happens. Also, in my original code the message did not refer to infinite types at all. Here is the relevant extract:
retrieveDomeinResourceDefinition :: forall e.
ResourceId
-> Namespace
-> (AsyncDomeinFile e (Either String PropDefs))
retrieveDomeinResourceDefinition id ns = do
df <- retrieveDomeinFile (namespaceToDomeinFileName ns)
case df of
e#(Left err) -> pure $ e
(Right f) -> case lookup id f of
Nothing -> pure $ Left ("retrieveDomeinResourceDefinition: cannot find definition of " <> id <> " in DomeinFile for " <> ns)
(Just propDefs) -> pure (Right propDefs)
retrieveDomeinFile :: forall e. Namespace -> AsyncDomeinFile e (Either String DomeinFile)
newtype PropDefs = PropDefs (StrMap Json)
Now here the compiler tells me: "Could not match type: PropDefs with type StrMap PropDefs..." It appears as if the compiler does not notice the lookup expression. In fact, when I replace "pure (Right propDefs)" with "pure (Right f)", the error goes away (and another appears on the line binding df, which I understand).
Now I write this up I notice the similarity between 'trying to match type t1 with type StrMap t1' and 'could not match type: PropDefs with StrMap PropDefs'. Still, I don't see why using e introduces this problem.
Using the value from a pattern match that way may be functionally equivalent, but the types are not - the type system doesn't have the evidence that the type variable for the Right can be ignored.
Using the types in your second example - when you pattern match e#(Left err), the type of e will be Either String DomeinFile - but you want an Either String PropDefs. The pattern match does not "free up" the type variable on the right, which is why you have to reconstruct the Left with the error again.
I'm wondering how to (safely) add type ascriptions to the lefthand side of Scala for expressions.
When working with complex for expressions, the inferred types are often hard to follow, or can go off track, and it's desirable to annotate the expressions within the for {..} block, to check that you and the compiler agree. An illustrative code sketch that uses the Eff monad:
for {
e1: Option[(Int, Vector[Int])] <- complexCalc
e2 <- process(e1)
} yield e2
def complexCalc[E]: Eff[E, Option[(Int, Vector[Int])]] = ???
However, this conflicts with Scala's support for pattern matching on the LHS of a for-expression.
As discussed in an earlier question, Scala treats the type ascription : Option[(Int, Vector[Int])] as a pattern to be matched.
The compiler has the concept of irrefutable patterns, which always match. Conversely, a refutable pattern might not match, and so when used in for expression, the righthand-side expression must provide a withFilter method to handle the non-matching case.
Due to a longstanding compiler bug/limitation, tuples and other common constructor patterns seem never to be treated as irrefutable.
The net result is that adding a type ascription makes the LHS pattern seem refutable and requires a filter op to be defined on the RHS. A symptom is compiler errors like value filter is not a member of org.atnos.eff.Eff[E,Option[(Int, Vector[Int])]].
If there any syntactic form that can be used to ascribe types to the intermediate expressions containing tuples, within for expressions, without encountering refutable pattern matching?
Turns out there's extensive discussion of this problem, and the current best solution, in this Scalaz PR *.
The work around is to add the type ascription in an assignment on the line below:
for {
e <- complexCalc
e1: Option[(Int, Vector[Int])] = e
e2 <- process(e1)
} yield e2
def complexCalc[E]: Eff[E, Option[(Int, Vector[Int])]] = ???
See this:
scala> 1 + 1
res0: Int = 2
scala> 1.+(1)
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
res1: Double = 2.0
scala> "a" :: List()
res2: List[String] = List(a)
scala> "a".::(List())
<console>:8: error: value :: is not a member of String
"a".::(List())
^
Why does the error occur?
Try this
List().::("a")
The reason is that :: is a method of List.
From ScalaByExample:
Like any infix operator, :: is also implemented as a method of an
object. In this case, the object is the list that is extended. This is
possible, because operators ending with a ‘:’ character are treated
specially in Scala. All such operators are treated as methods of their
right operand. E.g.,
x :: y = y.::(x) whereas x + y = x.+(y)
Note, however, that operands of a binary operation are in each case
evaluated from left to right. So, if D and E are expressions with
possible side-effects,
D :: E
is translated to
{val x = D; E.::(x)}
in order to maintain the left-to-right order of operand evaluation.
In scala methods which ends with : got applied in reverse order.
So when you write a::list it is actually list.::(a). String doesn't have :: method, so the solution is to write List().::("a") or Nil.::("a")
Because of operator precedence. In Scala methods which ends with : are right associative. So you should call List().::("a")
If you want to use left associative method then you should write List("a") ++ List(), but that's not always a good choice, cause it has linear execution time