scala

warning: Creating default object from empty value in /opt/bitnami/apps/portal/htdocs/modules/taxonomy/taxonomy.pages.inc on line 33.

JDK 17 on GitHub Actions

in

Here's a quick tutorial of how to test your project on JDK 17 using Ólaf's olafurpg/setup-scala. As the starting point we'll use the following setup, which is documented in Setting up GitHub Actions with sbt:

name: CI
on:
  pull_request:
  push:
jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            java: 11
            jobtype: 1
          - os: ubuntu-latest

intro to Scala 3 macros

in

Introduction

Macro is a fun and powerful tool, but overuse of the macro could cause harm as well. Please enjoy macros responsibly.

What is macro? A common explanation given is that a macro is a program that is able to take code as an input and output code. While it's true, it might not immediately make sense since Scala programmers are often familiar with higher-order functions like (map {...}) and by-name parameter, which on the surface it might seem like it is passing a block of code around.

Here's an example code from Expecty, an assertion macro that I ported to Scala 3:

scala> import com.eed3si9n.expecty.Expecty.assert
import com.eed3si9n.expecty.Expecty.assert
 
scala> assert(person.say(word1, word2) == "pong pong")
java.lang.AssertionError: assertion failed
 
assert(person.say(word1, word2) == "pong pong")
       |      |   |      |      |
       |      |   ping   pong   false
       |      ping pong
       Person(Fred,42)
 
  at com.eed3si9n.expecty.Expecty$ExpectyListener.expressionRecorded(Expecty.scala:35)
  at com.eed3si9n.expecty.RecorderRuntime.recordExpression(RecorderRuntime.scala:39)
  ... 36 elided

Had I used by-name argument for assert(...), I could control the timing of getting the result but all I'd get would be false. Instead with a macro, it's able to get the shape of source code person.say(word1, word2) == "pong pong", and programmatically generate the error message that includes the code and each of the values in the expression. Someone could potentially write a code that does that using Predef.assert(...) too, but that would be very tedious to do. This still doesn't cover the full aspect of macros.

A compiler is often thought of as something that translates some source code into a machine code. While certainly that is an aspect of it, a compiler does many more things. Among them is type checking. In addition to generating bytecode (or JS) at the end, Scala compiler acts as a lightweight proof system to catch various things like typos, and making sure that the parameter types are expected. The Java virtual machine is almost completely unaware of the Scala type system. This loss of information is sometimes referred to as type erasure, like it's a bad thing, but this duality of type and runtime enables Scala to exist at all as a guest programming language on JVM, JS, and Native.

For Scala, macro gives us a way to take actions at compile-time, and thus a way to directly talk with Scala's type system. For example, I don't think there's an accurate code one can write to detect if a given type A is a case class at runtime. Using macros this can be written in 5 lines:

import scala.quoted.*
 
inline def isCaseClass[A]: Boolean = ${ isCaseClassImpl[A] }
private def isCaseClassImpl[A: Type](using qctx: Quotes) : Expr[Boolean] =
  import qctx.reflect.*
  val sym = TypeRepr.of[A].typeSymbol
  Expr(sym.isClassDef && sym.flags.is(Flags.Case))

In the above ${ isCaseClassImpl[A] } is an example of Scala 3 macro, specifically known as splicing.

herding cats: day 19

in

Wrote herding cats: day 19 featuring FunctionK, or Rúnar's encoding of rank-2 polymorphic function, and Resource datatype, which he envisioned rank-N polymorphism would unlock back in 2010.

git bisecting scala/scala

in

git bisecting is a useful technique to locate the source of a bug. For scala/scala in particular, bisect.sh can save a lot of time by using the pre-build compiler artifacts on the Scala CI Artifactory.

scopt 4

in

scopt 4.0.0 is cross published for the following build matrix:

Scala JVM JS (1.x) JS (0.6.x) Native (0.4.0-M2) Native (0.3.x)
3.0.0-M2 n/a n/a n/a
3.0.0-M1 n/a n/a n/a
2.13.x n/a n/a
2.12.x n/a n/a
2.11.x

Here's how functional DSL looks like in scopt 4:

import scopt.OParser
val builder = OParser.builder[Config]
val parser1 = {
  import builder._
  OParser.sequence(
    programName("scopt"),
    head("scopt", "4.x"),
    // option -f, --foo
    opt[Int]('f', "foo")
      .action((x, c) => c.copy(foo = x))
      .text("foo is an integer property"),
    // more options here...
  )
}
 
// OParser.parse returns Option[Config]
OParser.parse(parser1, args, Config()) match {
  case Some(config) =>
    // do something
  case _ =>
    // arguments are bad, error message will have been displayed
}

Instead of calling methods on OptionParser, the functional DSL first creates a builder based on your specific Config datatype, and calls opt[A](...) functions that returns OParser[A, Config].

These OParser[A, Config] parsers can be composed using OParser.sequence(...).

virtualizing a hackathon at ScalaMatsuri 2020

in

Here's a report of running a virtual hackathon at ScalaMatsuri Day 2 Unconference. Someone proposed it for the Unconference, and I volunteered to be a facilitator on the day, so I went in without preparation. I booked the time originally for 4h (noon - 4pm JST, 11pm - 3am EDT) but it was successful so it got extended after some coffee break.

One thing I emphasize is The Law of Two Feet:

If at any time you find yourself in any situation where you are neither learning nor contributing: use your two feet and go someplace else

Equality in Scala

in

I gave a talk at ScalaMatsuri on 'Equality in Scala'

joining Twitter

in

I'm excited to announce that I'm joining Twitter's Build Team to work on the next generation of efficient build systems supporting thousands of Twitter developers worldwide. Today's my first day.

This is the team that developed monorepo build tool Pants, and is transitioning to migrate the flock to Bazel. This presented a unique opportunity for me to work with a team of people passionate about developer experience and productivity, and I'm looking forward to getting to know the team, and learning the new challenges.

I would also like to thank everyone who reached out to me during this transition period, often on DM, to check up on me, to stick up for me for an opportunity in their organization, and offering me projects to work on. You kept my spirits high. Thank you! Since my mandatory sabbatical started in April, I got to work on some of the projects that I previously didn't have time like build caching and Selective functor, and got to collaborate with wonderful folks at Scala Center, so it worked out in the end.

EE Build team is still hiring for "San Francisco, Remote US" location, so if that sounds interesting to you, I'd happy to talk to you.

Jar Jar Abrams

in

Jar Jar Abrams is an experimental Scala extension of Jar Jar Links, a utility to shade Java libraries.

For library authors, the idea of other library is a double-edged sword. On one hand, using other libraries avoids unnecessary duplication of work, not using other libraries is almost hypocritical. On the other hand, each library you add would add a transitive dependency to your users, increasing the possibility of conflict. This is partly due to the fact that within a single running program you can one have one version of a library.

user-land compiler warnings in Scala, part 2

in

Last week I wrote about #8820, my proposal to add user-land compiler warnings in Scala. The example I had was implementing ApiMayChange annotation. This was ok as a start, but a bit verbose. If we want some API status to be used frequently, it would be cool if library authors could define their own status annotation. We're going to look into doing that today.

Syndicate content