Starlark 4.2.1

in

Starlark is a dialect of Python, originally designed as a configuration language for the Bazel build tool. Currently there are implementations in Go, Java, and Rust. As far as I know, the main Java implementation of Starlark has only been available as Bazel's source repo on GitHub.

Since it would be convenient to have a binary distribution, I've forked the repo, and published it as "com.eed3si9n.starlark" % "starlark" % "4.2.1" (com.eed3si9n.starlark:starlark:4.2.1) on Maven Central. The code is the same as Bazel 4.2.1.

scalaxb 1.8.2

scalaxb 1.8.2 is released.

sbt 1.6.0-M1

in

Hi everyone. On behalf of the sbt project, I am happy to announce sbt 1.6.0-M1. This is the first milestone (M1) of the 1.6.x feature release, a binary compatible release focusing on new features. sbt 1.x is released under Semantic Versioning, and the plugins are expected to work throughout the 1.x series. Please try it out, and report any issues you might come across.

The headline features of sbt 1.6.0 are:

  • Improved JDK 17 support
  • BSP improvements
  • Remote caching improvements
  • Zinc improvements

How to upgrade

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.

Jar Jar Abrams 1.8.0 and sbt-assembly 1.1.0

in

Jar Jar Abrams 1.8.0 and sbt-assembly 1.1.0 are released.

Jar Jar Abrams is an experimental extension to Jar Jar Links, intended to shade Scala libraries. Thus far we have been using Pants team's fork of Jar Jar Links, but now that it's been abandaned, Eric Peters has in-sourced it to jarjar-abrams repo so we can patch it.

Our jarjar fork is released under com.eed3si9n.jarjar organization name and package name.

bug fixes

  • Eric has fixed a bug around ShadeRules.keep.

enhancement

  • ASM was updated to 9.2.

sudori part 2

in

I'm hacking on a small project called sudori, an experimental sbt. The initial goal is to port the macro to Scala 3. It's an exercise to take the macro apart and see if we can build it from the ground up. This an advanced area of Scala 2 and 3, and I'm finding my way around by trial and error. This is part 2.

  • contImpl macro implements both Applicative-do and Monadic-do. It does so by first scanning the abstract syntax tree for key.value. When there are none, it calls pure(...) and map(...) when there is exactly one.
  • To pass the Instance instance i to macro and back to the generated code, it uses Singleton type i.type, which internally holds on to the symbol of i.
  • Quotes API provides a convenient way to generating lambda expression, but it must know the exact type of the parameters. This means we can't gradually expand the definition of the lambda expression as we walk the tree. To workaround this, we will use var one scope outside of the lambda expression.
  • We defined Zero typeclass and summon a value for it to initialize the vars.

sudori part 1

in

I'm hacking on a small project called sudori, an experimental sbt. The initial goal is to port the macro to Scala 3. It's an exercise to take the macro apart and see if we can build it from the ground up. This an advanced area of Scala 2 and 3, and I'm finding my way around by trial and error.

Reference:
- Scala 3 Reference: Metaprogramming

sbt 1.5.5

in
Syndicate content