Scala on Android: Database cursors made easy

Ever wanted to iterate over Android cursor in the scala way ? This article will explain how with scala 2.11 we can write a library to do this awesome calls.

import AugmentedCursor._

val uri =
  Uri.parse("content://com.android.calendar/events")
val cursor = getContentResolver().query(uri,
  Array("calendar_id", "title", "description"),
  null, null, null)
for((calendar_id, title, description) <-
     cursor.toIterator[Int, String, String]) {
   /// ... Do something with calendar_id, title
   ///     and description for each entry
}

This is so much better than writing complicated code, and besides, it provide types  for the iteration variables !

Here is how we can make this magic happen.

First, we need to create an implicit class so that the method .toIterator[Int, String, String] can be defined.

object AugmentedCursor {
  implicit class RichCursor(c: Cursor) {
    def toIterator[T, U, V] = ???
  }
}

Now, the method toIterator should return an Iterable[(T, U, V)] to be able to write the initial code. Thus:

implicit class RichCursor(c: Cursor) {
  def toIterator[T, U, V] = new Iterable[(T, U, V)] {
    def iterator = ???
  }
}

The iterator itself must be an iterator over the triplet. Since we will only call it once and then close it, we can inline its construction.

implicit class RichCursor(c: Cursor) {
  def toIterator[T, U, V] = new Iterable[(T, U, V)] {
    def iterator = new Iterator[(T, U, V)] {
      def hasNext: Boolean = ???
      def next: (T, U, V) = ???
    }
  }
}

The hasNext method can compare the position of the cursor against the length of the result. Since during iterations, hasNext is called only once per iteration, we can close the cursor when there is nothing more to fetch.

def hasNext: Boolean = {
  val res = c.getPosition < c.getCount - 1
  if(!res) c.close()
  res
}

The method next is as simple, it will move the cursor to one place and then extract the right positions.

def next: (T, U, V) = {
  c.moveToNext()
  ???
}

Here we have a new problem. There is no such method as c.get[T](0) (take the first element of the cursor, and cast it to T). To the contrary, there are some methods such as c.getString, c.getLong, c.getInt, etc.

This is the time to set up a constraint: T, U and V can only be of type that the cursor can extract. We can enfore this by requiring that there exist implicit objects which can extract the type T out the cursor. For that, we define the trait Iter and two implicit objects implementing it, in the top-level object:

trait Iter[T] { def apply(c: Cursor, i: Int): T
implicit object IterS extends Iter[String] {
  def apply(c: Cursor: i: Int):String = c.getString(i)
}
implicit object IterI extends Iter[Int] {
  def apply(c: Cursor: i: Int):Int = c.getInt(i)
}

Nice, but how to be able to include them now in the next function? Simply, we can use implicitly[Iter[T]] to retrieve the implicit object and call its apply method. Sorry, we cannot use the syntactic sugar of removing the word "apply" since the method implicitly does not take parameters and this would be ambiguous. Thus:

def next: (T, U, V) = {
  c.moveToNext()
  (implicitly[Iter[T]].apply(c,0),
   implicitly[Iter[U]].apply(c,1),
   implicitly[Iter[V]].apply(c,2))
}

Now the compiler complains that there is no guarantee that there is an implicitly[Iter[T]].  How can we ensure that so that T is restrained? There is a funcitonality in scala: implicit parameters.  If the compiler can resolve them, no need to specify them ! This means that the method toIterator[T, U, V] introducing the three  type parameters requires the implicit objects to work, that is:

def toIterator[T, U, V](implicit objT: Iter[T], objU: Iter[U], objV: Iter[V])

There is also an even better syntax for that in scala, syntactic sugar:

def toIterator[T: Iter, U: Iter, V: Iter]

What's next? Abstraction ! We did it for three parameters, but perhaps we want to have one, two or more ! The refactoring is left as an exercise for the reader. For the impatient reader, the solution is below:

You need to  to get the syntactic sugar above. Voilà !

// AugmentCursor.scala
import android.database.Cursor
object AugmentedCursor {
  trait Iter[T] {
    def apply(cursor: Cursor, index: Int): T
  }
  implicit object IterInt extends Iter[Int] {
    def apply(c: Cursor, i: Int) = c.getInt(i)
  }
  implicit object IterString extends Iter[String] {
    def apply(c: Cursor, i: Int) = c.getString(i)
  }
  implicit class RichCursor(c: Cursor) {
    private def implicitIterator[T](f: => T) = new Iterator[T] {
      def hasNext = {
        val res = c.getPosition < c.getCount - 1
        if(!res) c.close()
        res
      }
      def next() = {
        c.moveToNext()
        f
      }
    }
    private def implicitIter[T](f: => T) = new Iterable[T] {
      def iterator = implicitIterator[T] {
        f
      }
    }
    def toIterator[T : Iter] = implicitIter {
      implicitly[Iter[T]].apply(c, 0)
    }
    def toIterator[T : Iter, U: Iter] = implicitIter {
      (implicitly[Iter[T]].apply(c, 0),
       implicitly[Iter[U]].apply(c, 1))
    }
    def toIterator[T : Iter, U: Iter, V: Iter] = implicitIter {
      (implicitly[Iter[T]].apply(c, 0),
       implicitly[Iter[U]].apply(c, 1),
       implicitly[Iter[V]].apply(c, 2))
    }
 }

 

Converting Javascript to Scala on Scala-js

Besides the lessons (for functions) given by Sebastian, the author of Scala-js, here are some more lessons I learned while porting javascript code to Scala to use Scala-js.

  • Wrap your code. First, wrap your code in an object's body. You need this since scala does not authorize code outside of objects or classes. Extend JSApp to benefit from variables such as document, window, etc.
    object YourApp extends JSApp { ... }
    Put your code inside the brackets.
  • Object literals: To convert the following javascript snippet:
    { action: "bind", data=42, check=true }
    you can use the literal import and use one of the following two constructions:

    import js.Dynamic.{literal => l}
    l(action="bind", data=42, check=true)
    l("action"->"bind", "data"->42, "check"->true)
  • Order of val/var/def: The order of var/val and defs inside an anonymous function matter. You will have to correct all back-references. Hence, the following:
    function changeValue() { value = 5; }
    var value = 0

    which is valid in javascript, will has to be rewritten as

    var value = 0
    function changeValue() { value = 5; }
  • Implicit converters from Dynamic to Boolean: Because js.Dynamics are sometimes used in boolean conditions or for comparison, I added implicit converters so that it does not throw errors.
    object Implicits {
      implicit def dynamicToBoolean(d: js.Dynamic): Boolean = d.asInstanceOf[Boolean]
      implicit def dynamicToString(d: js.Dynamic): String = d.asInstanceOf[String]
    }
  • Warning comparing dynamic with strings: To remove the annoying warnings which arrive when you compare dynamic with strings, you can add the following implicit:
  • object Implicits {
      implicit class ComparisonOp(d: js.Dynamic) {
        def ==(other: String) = d.asInstanceOf[String] == other
      }
    }
  • Use of the javascript "in" keyword
    if(key in obj) has to be translated to if(!js.isUndefined(obj(key)))
  • for...in
    for(a in obj) has to be translated to:
    for(a <- js.Object.keys(obj))
  • String extraction: Instead of extracting chars with
    string(index)
    prefer the following
    string.substring(index, 1)
    which returns a String instead of a Char. It is hard to convert from Char to String.
  • Zero-argument functions: Careful when you translate zero-arg functions to def. If you pass them as parameter, you need to add the modifier _ so that they are not evaluated. For example:
    function loadSomething() { ... }
    $("#button").change(loadSomething)

    has to be translated to:

    def loadSomething() { ... }
    $("#button").change(loadSomething _)
  • setTimeout:  You will have to translate setTimeout using another construct, with a lazy evaluated block. Thus, this:
    setTimeout(function() { ... }, 999)
    becomes:
    js.timers.setTimeout(999){ ... }Do not put anonymous function inside the brackets like { () => ...} else they the function body will never be evaluated !
  • console.log: In order to use console.log directly in your code, import the following which provides a statically typed object
    import org.scalajs.dom._
    console.log("Whatever")

    or alternatively, you can use the dynamic global variable:

    import js.Dynamic.{global => g}
    g.console.log("Whatever")
  • Call object's main method. Last but not least. If you define your methods in an object, especially some jQuery onLoad event, you need to call at least one method from the object for it to be initialized. Indeed, objects are lazily created.
    For example, if your initializer object is:

    package your.app
    import scala.scalajs.js.JSApp
    import org.scalajs.jquery.{jQuery => $}
    object YourApp extends JsApp {
      $(document).ready(() => { ...
       })
     def main() = {}
    }

    you need to include a script in your html like this:
    <body onload="your.app.YourApp().main()">

  • Bracket access on custom objects. For the call point["name"] with objects like the following, add a method for custom bracket access. You may factorize this method in a custom trait.
    val point = new Object {
      val name = "Mikael"
      val age = 18
      @JSBracketAccess
      def apply(index: String): js.Dynamic = js.native
    }
    //point("name") will retrun "Mikael" as a js.Dynamic
  • String-to-int conversion:
    • If you use JQuery and  observe $("something").value()*1, since  value() returns a js.Dynamic, simply converting this to $("something").value().toInt will fail at run-time (the toInt method is not defined on object of type string). However, you can do the following:
      $("something").value().asInstanceOf[String].toInt
    • If you find the pattern 1*s , where s is a string which might be null, don't use s.toInt directly in scala. It fails if s is null, whereas 1*null  == 0 in javascript. Instead, do the following:
      implicit class OrIfNull[T](s: T) {
        def orIfNull(e: T): T = if(s == null) e else s
      }
      s.orIfNull("0").toInt

Remember, next time, to directly start with scala-js 🙂

Integrating Scala-JS into an existing project, part 2

After integrating basic scalajs support into the project (part 1), we now want to include the following libraries (described here):

  • dom for manipulating the DOM structure of the HTML page
  • jquery to have an easier access to the DOM. And of course, we want to use the shortcut "$" instead of the verbose jQuery

To do so, add the following settings to your mainprojectfolder/build.sbt file describing the js project defined in the last article.

lazy val js = (project in file("js")).enablePlugins(ScalaJSPlugin).settings(
  libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "0.8.0",
  libraryDependencies += "be.doeraene" %%% "scalajs-jquery" % "0.8.0"
)

This is the safest way to ensure that the libraries are imported.
Now in your scala file compiling to javascript (myscript.scala), you can include these import statements:

package your.package
import org.scalajs.dom
import dom.document
import org.scalajs.jquery.{ jQuery => $ }
import scala.scalajs.js.annotation.JSExport

object YourApp extends JSApp {
  @JSExport def addClickedMessage(): Unit = {
    val message = "You clicked the button!"
    //appendPar(document.body, message) (DOM version)
    $("body").append(s"<p>$message</p>")
  }
 
  def main(): Unit = {
    println("Hello world!")
  }
}

Now in your html you can include something like this, and this will work:

<button id="click-me-button" type="button" onclick="your.package.YourApp().addClickedMessage()">Click me!</button>

I hope that you will enjoy Scala-js as much as I do !

Integrating Scala-js into an existing project, Part 1

It can be useful to get rid of Javascript and switch to a better typed language such as Typescript or Scala. Both compile to Javascript.

In this blog post, I will describe how work with scala-js to transform the existing javascript files of a project to scala and have the compilation pipeline work as usual.

Assuming you are working with sbt > 0.13, and your project is inside the folder mainprojectfolder, do the following:

    1. Make sure the file mainprojectfolder/project/plugins.sbt exists and contains the following line :
      addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.4")
    2. Modify the build.sbt file of your top-level project to compile the javascript project when it is built. Add the following lines:
      lazy val js = (project in file("js")).enablePlugins(ScalaJSPlugin)
      scalaVersion in js := "2.11.1" // Or the right version
      addCommandAlias("runWithJS", ";fastOptJS;run")
    3. Make sure the definition of the root includes the following aggregation keyword.
      lazy val root = project(...).aggregate(js)
    4. Create your scala file(s) at  mainprojectfolder/js/src/main/scala/your/package/myscript.scala with the following content (or something from the tutorial):
    5. package your.package
      import scala.scalajs.js.JSApp
      object YourApp extends JSApp {
        def main(): Unit = {
          println("Hello world!")
        }
      }

      Now, after running sbt  inside mainprojectfolder I, you can launch the following  new tasks:
      fastOptJS  which compiles the new scala file above to javascript
      runWithJS  which performs the fastOptJS  above plus the run command (you can customize this according to your needs).

      These two tasks both create the following files inside mainprojectfolder/js/target/scala-2.11 :
      jsproject-fastopt.js: This file needs to be included in the final html.
      jsproject-fastopt.js.map: This is the source map
      jsproject-jsdeps.js: This file needs to be included in the final html. It contains all the remaining dependencies.

    6. You may be interested to copy the generated javascript files to another folder like mainprojectfolder/public/js. For that, add the following line in the build.sbt file:
      lazy val copyjs = TaskKey[Unit]("copyjs", "Copy javascript files to target directory")
      copyjs := {
        val outDir = baseDirectory.value / "public/js"
        val inDir = baseDirectory.value / "js/target/scala-2.11"
        val files = Seq("js-fastopt.js", "js-fastopt.js.map", "js-jsdeps.js") map { p =>   (inDir / p, outDir / p) }
        IO.copy(files, true)
      }

      and of course update the custom command in the same file:

      addCommandAlias("runWithJS", ";fastOptJS;copyjs;run")

Next: Part 2: Integrating DOM and jQuery