An experimental Agda backend that generates Scala 2 or Scala 3 source files.
Currently supported:
- ✅ Sum types / ADTs
- ✅ simple ADTs
- ✅ polymorphic ADTs, e.g.
Maybe[A],List[A] - ✅ constructors with arguments
- ✅ Product types / records
- ✅ simple records
- ✅ records with polymorphic parameters
- ✅ Simple and polymorphic functions, e.g.
id[A] - ✅ Literals:
Int/Nat,Bool,String(subset) - ✅ Function application
- ✅ normal visible arguments
- ✅ erased hidden/type-level Agda arguments
- ✅ Pattern matching on constructors
- ✅ zero-arity constructors
- ✅ flat constructor patterns with arguments
- ✅ constructor branch binders
- 🚧 nested constructor patterns
- 🚧 literal branches / catch-all branches
- ✅ Basic conditionals and operators
- ✅ Agda
if_then_else_lowered to Scalaif ... then ... else ... - ✅ selected binary operators
- ✅ natural-number boolean comparison
_ <ᵇ _lowered to Scala<
- ✅ Agda
- ✅ 🚧 Mapping selected Agda stdlib names to Scala stdlib-like output
- ✅
Nat - ✅
List - ✅ pairs / products
- 🚧 broader stdlib coverage
- ✅
Roadmap is captured in milestones:
- support polymorphic ADTs: List, Red Black Tree, ZIO: #17
- support FP abstractions: Functor, Monad: #12
cabal run -- agda2scala ./examples/adts.agdacompile following Agda code:
module examples.rbt where
open import Data.Bool using (Bool; true; false; if_then_else_)
open import Data.Nat using (ℕ; _<ᵇ_)
data Color : Set where
Red Black : Color
{-# COMPILE AGDA2SCALA Color #-}
data RedBlackTree (V : Set) : Set where
EmptyRBT : RedBlackTree V
RBT : Color -> RedBlackTree V -> ℕ -> V -> RedBlackTree V -> RedBlackTree V
{-# COMPILE AGDA2SCALA RedBlackTree #-}
lookup : {V : Set} -> V -> ℕ -> RedBlackTree V -> V
lookup defaultVal key EmptyRBT =
defaultVal
lookup defaultVal key (RBT c left currKey x right) =
if key <ᵇ currKey then
lookup defaultVal key left
else
if currKey <ᵇ key then
lookup defaultVal key right
else
x
{-# COMPILE AGDA2SCALA lookup #-}into Scala code:
package examples
object rbt:
enum Color:
case Red
case Black
enum RedBlackTree[V]:
case EmptyRBT extends RedBlackTree[Nothing]
case RBT[V](x0: Color, x1: RedBlackTree[V], x2: Long, x3: V, x4: RedBlackTree[V]) extends RedBlackTree[V]
def lookup[V](x1: V, x2: Long, x3: RedBlackTree[V]): V = x3 match
case RedBlackTree.EmptyRBT =>
x1
case RedBlackTree.RBT(p0, p1, p2, p3, p4) =>
if x2 < p2 then
lookup(x1, x2, p1)
else if p2 < x2 then
lookup(x1, x2, p4)
else
p3More Agda examples and output Scala 3 code.
We use 2 internal representation
TCM glue prettyPrint
Definition ==========> AgdaIR ==> ScalaIR ============> String
At the end we choose between Scala 2 and Scala 3 syntax. Possible future directions: Kotlin, Java, JVM bytecode
Agda constructor names are not always valid Scala identifiers (e.g. []).
We apply a small mapping table for common constructors and then sanitize the result:
[]→Nil_∷_/_::_/_cons_→Cons_,_→Pair
See Agda.Compiler.Scala.Name.NamePolicy.
- continuous compilation loop using entr
find -name '*.hs' | entr cabal test all
find . -name '*.hs' | entr cabal test agda2scala-test
find . -name '*.hs' | entr cabal test agda2scala-propsor using ghcid
ghcid- Build
cabal build all- Run tests
cabal test all
cabal test agda2scala-test
cabal test agda2scala-props- Simple way to run Scala backend
cabal run -- agda2scala --help
cabal run -- agda2scala ./examples/adts.agda- Generate Scala2 output
cabal run -- agda2scala --compile --no-main --scala-dialect=Scala2 --out-dir=scala2/src/main/scala ./examples/adts.agda- Generate Scala3 (dotty) output
cabal run -- agda2scala --compile --no-main --scala-dialect=Scala3 --out-dir=scala3/src/main/scala ./examples/adts.agdacabal run -- agda2scala --help
cabal run -- agda2scala ./examples/adts.agda
cabal run -- agda2scala --compile --no-main --scala-dialect=Scala2 --out-dir=scala2/src/main/scala ./examples/adts.agda- format code
fourmolu -i $(find src app test -name '*.hs')
cabal-fmt -i agda2scala.cabal- static code analysis using HLint:
hlint src app testThere are Scala 3 and Scala 2 projects for code generated from Agda examples
They have unit tests, that use code generated from examples.
agda2scala
(generate) sbt test
Agda examples ==============> src/main/scala <============ src/test/scala
checks:
* compile Agda examples to Scala code
* run Scala unit tests that calls that Scala code
Those tests are run on CI - Github Actions.
Generate Scala 2 code from Agda examples and running tests:
cabal run -- agda2scala --compile --no-main --scala-dialect=Scala2 --out-dir=scala2/src/main/scala ./examples/adts.agda
cabal run -- agda2scala --compile --no-main --scala-dialect=Scala2 --out-dir=scala2/src/main/scala ./examples/rbt.agda
cd scala2
sbt ~testgenerate Scala 3 code:
cabal run -- agda2scala --compile --no-main --scala-dialect=Scala3 --out-dir=scala3/src/main/scala ./examples/adts.agda
cabal run -- agda2scala --compile --no-main --scala-dialect=Scala3 --out-dir=scala3/src/main/scala ./examples/rbt.agda
cd scala3
sbt ~test- Documentation for Agda as Haskell library on Hackage including
- docs for Agda.Compiler.Backend
- build-in JS backend
- build-in Haskell backend
- other backends: