I never really used Traits much in Scala so far, and I want to change this. I have this code:
import tools.nsc.io.Path
import java.io.File
trait ImageFileAcceptor extends FileAcceptor {
override def accept(f:File) = {
super.accept(f) match {
case true => {
// additional work to see if it's really an image
}
case _ => false
}
}
}
The problem is, when I compile with sbt, I keep receiving:
ImageFileAcceptor.scala:2: ';' expected but 'import' found.
If I add ; after the imports, the code compiles. Here's FileAcceptor:
import java.io.File
trait FileAcceptor extends Acceptable {
override def accept(f:File):Boolean = f.isFile
}
And here's Acceptable:
import java.io.File
trait Acceptable {
def accept(f:File):Boolean
}
I don't understand why I need semicolons after the imports.
Maybe the output of sbt is helpful:
[info] Building project tt 1.0 against Scala 2.8.1
[info] using sbt.DefaultProject with sbt 0.7.5 and Scala 2.7.7
When the scala compiler encounters a Macintosh line ending--being \r--the scala compiler will erroneously declare the need for a semi-colon, as Moritz so deduced. Section 1.2 of the Scala Reference Manual describe correct newline characters. I could not find in the Reference which character literals were considered as newlines. From experience, both Windows (\r\n) and Unix (\n) are acceptable. Presumably scala is strictly compatible with Java in this regard.
Related
I want release library for 2.12 and 2.13 scala version. But it depends on another library, which exist for 2.12 only. For 2.13 I wrote my implementation for fast function:
2.12 code looks:
import com.dongxiguo.fastring.Fastring.Implicits._ //2.12 only
object Lib {
val a = fast"hello world"
}
2.13 code looks:
import mycompat.Implicits._ //2.13 only
object Lib {
val a = fast"hello world" //my implementation
}
So difference - only import ... in several files.
I can't understund how to organize code for differents scala version.
Having different imports is problematic, because that means you have different sources (and you need to maintain them). I think providing missing implementation of library in it's own original package will be better solution.
//main/scala-2.13/com/dongxiguo/fastring/Fastring/Implicits.scala
package com.dongxiguo.fastring.Fastring
object Implicits {
//your implementation of fast"Something"
}
As long as it is in scala-2.13 folder it will be compiled and used only for scala-2.13.
You need also different dependencies for 2.12 and 2.13 versions:
libraryDependencies ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 12)) => Seq("com.dongxiguo" %% "fastring" % "1.0.0")
case Some((2, 13)) => Seq()
case _ => Seq()
}
}
You will have same Lib implementation without any change for scala 2.13 and when fastring will be released for new scala version You will just remove those parts.
You can also create your own proxy object that will have distinct implementations for 2.12 and 2.13 in mycompat.Implicits._.
//main/scala-2.13/mycompat/Implicits.scala
package com.mycompat
object Implicits { /* proxy to fast"Something" from fastring library */ }
//main/scala-2.12/mycompat/Implicits.scala
package com.mycompat
object Implicits { /* your implementation of fast"Something" */ }
This is also good idea.
Based on lihaoyi and What are the Scala version-specific source directories in sbt? try something like so
src/main/scala/example/Hello.scala:
package example
object Hello extends Greeting with App {
println(greeting)
}
src/main/scala-2.11/example/Greeting.scala:
package example
trait Greeting {
lazy val greeting: String = "hello-from-2.11.12"
}
src/main/scala-2.13/example/Greeting.scala:
package example
trait Greeting {
lazy val greeting: String = "hello-from-2.13.1"
}
build.sbt:
crossScalaVersions := List("2.13.1", "2.11.12")
Now sbt ++2.11.12 run outputs
hello-from-2.11.12
whilst sbt ++2.13.1 run outputs
hello-from-2.13.1
In my scala source files
File.scala
#casejsTraitNative trait Variables extends js.Object {
val first: Int
}
case class Model(in:String)
I want to replace traits with #casejsTraitNative with some boiler plate during compile time
Expected Result : File.scala
trait Variables extends js.Object {
val first: Int
}
object Variables {
#inline def apply(first: Int): Variables = {
val p = FunctionObjectNativeMacro()
p.asInstanceOf[Variables]
}
def copy( source: Variables, first: OptionalParam[Int] = OptDefault): Variables = {
val p = FunctionCopyObjectNativeMacro()
p.asInstanceOf[Variables]
}
}
case class Model(in:String)
I can create expected source string using scalameta,but i don't know which sbt task i need to hook to modify source files before they passed to compiler...
As pointed out by Seth Tisue, the way to fix this is by modifying the sbt source generators keys in your project http://www.scala-sbt.org/1.x/docs/Howto-Generating-Files.html. You can there use Scalameta if you use sbt 1.x. Otherwise, you can't because Scalameta does not cross-compile to 2.10.x, the Scala version sbt 0.13.x uses.
Another way of solving this problem is annotation macros. Have a look at http://docs.scala-lang.org/overviews/macros/annotations.html, but that's more complicated.
All in all, I think the best solution is to use Paiges (https://github.com/typelevel/paiges). It's a little bit more constrained that Scala Meta, but it should allow you to generate that code and more.
I have a 3rd party library with package foo.bar
I normally use it as:
import foo.bar.{Baz => MyBaz}
object MyObject {
val x = MyBaz.getX // some method defined in Baz
}
The new version of the library has renamed the package from foo.bar to newfoo.newbar. I have now another version of my code with the slight change as follows:
import newfoo.newbar.{Baz => MyBaz}
object MyObject {
val x = MyBaz.getX // some method defined in Baz
}
Notice that only the first import is different.
Is there any way I can keep the same version of my code and still switch between different versions of the 3rd party library as and when needed?
I need something like conditional imports, or an alternative way.
The other answer is on the right track but doesn't really get you all the way there. The most common way to do this kind of thing in Scala is to provide a base compatibility trait that has different implementations for each version. In my little abstracted library, for example, I have the following MacrosCompat for Scala 2.10:
package io.travisbrown.abstracted.internal
import scala.reflect.ClassTag
private[abstracted] trait MacrosCompat {
type Context = scala.reflect.macros.Context
def resultType(c: Context)(tpe: c.Type)(implicit
tag: ClassTag[c.universe.MethodType]
): c.Type = {
import c.universe.MethodType
tpe match {
case MethodType(_, res) => resultType(c)(res)
case other => other
}
}
}
And this one for 2.11:
package io.travisbrown.abstracted.internal
import scala.reflect.ClassTag
private[abstracted] trait MacrosCompat {
type Context = scala.reflect.macros.whitebox.Context
def resultType(c: Context)(tpe: c.Type): c.Type = tpe.finalResultType
}
And then my classes, traits, and objects that use the macro reflection API can just extend MacrosCompat and they'll get the appropriate Context and an implementation of resultType for the version we're currently building (this is necessary because of changes to the macros API between 2.10 and 2.11).
(This isn't originally my idea or pattern, but I'm not sure who to attribute it to. Probably Eugene Burmako?)
If you're using SBT, there's special support for version-specific source trees—you can have a src/main/scala for your shared code and e.g. src/main/scala-2.10 and src/main/scala-2.11 directories for version-specific code, and SBT will take care of the rest.
You can try to use type aliases:
package myfoo
object mybar {
type MyBaz = newfoo.newbar.Baz
// val MyBaz = newfoo.newbar.Baz // if Baz is a case class/object, then it needs to be aliased twice - as a type and as a value
}
And then you may simply import myfoo.mybar._ and replace the object mybar to switch to different version of the library.
I tried to run one example from Programming in Scala but compiler gives me error:
Description Resource Path Location Type
illegal start of statement (no modifiers allowed here) ChecksumAcc.sc /HelloWorld/src line 3 Scala Problem
basically complains about private
import scala.collection.mutable.Map
object ChecksumAcc {
private val cache = Map[String, Int]()
}
I'm using Eclipse for Scala worksheet. Same after updating. I believe it uses 2.9.3 scala compiler. Why doesn't it compile?
Not sure what your actual question is, but the Scala worksheet has some special rules (as indicated by the very clear error message...). One thing you can do if you have to use the worksheet, is to put all your code inside a Worksheet object like this:
object Worksheet {
import scala.collection.mutable.Map
object ChecksumAcc {
private val cache = Map[String, Int]()
}
}
Or alternatively, use Eclipse's "New Scala object..." and use that instead of the worksheet.
To avoid the error message you are seeing, when you are working in a Eclipse Scala work sheet
wrap the Class definition and Companion class (Singleton object) in the same object
object worksheet {
class CheckSumAccumulator {
...
}
object CheckSumAccumulator {
...
}
CheckSumAccumulator.calculate("foobar")
}
I'm getting a compilation failure using the Maven Scala plugin that I'm not getting using The Eclipse Scala IDE. First the code:
package com.example
trait SomeTrait[OUTPUT, INPUT] {
def apply(request: INPUT, f: OUTPUT => Unit);
}
class SomeClass extends SomeTrait[String,String] {
def apply(request, f) {
f.apply(request.toUpperCase())
}
}
object App extends Application {
override def main(args: Array[String]) {
new SomeClass()("test", (value) => { println(value)})
}
}
Eclipse Scala IDE is fine but Maven gives me this error:
[ERROR] .../src/main/scala/com/example/App.scala:8:
error: ':' expected but ',' found.
[INFO] def apply(request, f) {
[INFO] ^
[ERROR] .../src/main/scala/com/example/App.scala:11:
error: identifier expected but '}' found.
[INFO] }
If I specify the types, as in:
class SomeClass extends SomeTrait[String,String] {
def apply(request: String, f: String => Unit) {
f.apply(request.toUpperCase())
}
}
It compiles in both.
Versions etc.:
Scala version: 2.8.1
Scala IDE for Eclipse: 1.0.0.201011130651
Maven: 3.0
Maven Scala Plugin: 2.15.0
Java: 1.6.0_17
Eclipse is wrong here ... you should be seeing the same compile time error there as you do in the command-line case.
What's baffling is that the Scala compiler embedded in Eclipse (which is just scalac 2.8.1.final running in an incremental compilation mode) is managing to successfully compile the source you originally provided and is inferring the argument types that you want ... classfiles are generated, and in the binary output SomeClass.apply has the signature you would expect.
This just shouldn't be happening.
UPDATE:
As Paul noted the IDE is behaving as if -Yinfer-argument-types were enabled. It's also behaving as if -Ydependent-method-types were enabled. Which suggests that there's something awry with the new logic behind the -Xexperimental option which manifests itself when compiler Settings instances are created in the way that the IDE does it.
You're using -Yinfer-argument-types wherever it's working.