- type level programming using types in a tuple
- override apply for the function syntax(functors instead of constructors?)
- 'tagless final' is a design pattern that allows type checking interfaces at compile via an algebra (trait) of given higher kinded case class of type Algebra[Expression].
- rcardin/yaes for 'direct style' scala based on phatom types and capabilities
- for comprehensions for monad, io effect systems or futures
- coursier for cli management
- native-image need -march=compatability
coursier install metals
then coursier install bloop
guix shell --network --container --emulate-fhs bash which zlib sed openjdk:jdk openjdk ncurses coreutils findutils curl grep nss-certs gcc-toolchain pkg-config git
- zlib/which for scala and sed for sbt
- dev-ex:
guix shell --preserve='^DISPLAY$' --preserve='^XAUTHORITY$' --network --container --emulate-fhs --share=$HOME/temphome=$HOME --expose="${XAUTHORITY}" --share=$HOME/.guix-home=$HOME/.guix-home --share=$HOME/.config/emacs=$HOME/.config/emacs --share=/gnu/store=/gnu/store bash which zlib sed openjdk:jdk openjdk coreutils curl grep nss-certs gcc-toolchain pkg-config git openssh emacs-next aspell aspell-dict-en gnupg guile tree-sitter-rust tree-sitter-yaml tree-sitter-python tree-sitter-scala emacs-gptel emacs-eat emacs-debbugs emacs-org-roam emacs-guix emacs-osm emacs-minions emacs-transmission emacs-undo-tree emacs-dape emacs-macrostep-geiser emacs-geiser-guile emacs-flymake-guile emacs-pyvenv emacs-scala-mode emacs-sbt-mode emacs-scala-ts-mode
- start metals
coursier bootstrap --java-opt -XX:+UseStringDeduplication --java-opt -Dmetals.startMcpServer=true org.scalameta:metals_2.13:1.5.2 -o metals -f
- Set port with in the
.metals/mcp.json
config server port {"metals": { "startMcpServer": true, "mcpPort": 9040 }}
?
{
"servers": {
"/gnu/git/scala-test/scala-3-project-template/-metals": {
"url": "http://localhost:39497/sse"
}
}
}
val scala3Version = "3.7.0"
lazy val root = project
.in(file("."))
.settings(
name := "Scala 3 Project Template",
version := "0.1.0-SNAPSHOT",
scalaVersion := scala3Version,
libraryDependencies ++= Seq( // %% adds _3 for scala versions
"com.lihaoyi" %% "upickle" % "4.2.1",
"com.lihaoyi" %% "upickle-implicits-named-tuples" % "4.2.1", // upickle named tuples as json
"org.scala-lang" %% "scala2-library-cc-tasty-experimental" % "3.7.0", // add capture checking to stdlib
"org.scalameta" %% "munit" % "1.0.0" % Test))
import language.experimental.captureChecking
case class Pooled(count: Int, msg: String):
override def toString: String = count.toString + msg
val stack = scala.collection.mutable.ArrayBuffer[Pooled]()
var nextFree = 0
def withFreshPooled[T](op: Pooled^ => T, msg: String): T = // remove ^ and val poolRejectClosure = withFreshPooled(pooled => () => pooled.toString ) will print garbage instead of be rejected at compile time ex: Main$package$$$Lambda/0x00007f0e8d340ee0@1ae557e0
if nextFree >= stack.size then stack.append(new Pooled(nextFree+1, msg)) // grow stack by 1 if we need more room and save out count/index++ objects are reused
val pooled = stack(nextFree)
nextFree = nextFree + 1
val ret = op(pooled)
nextFree = nextFree - 1
ret
import upickle.default.ReadWriter.join
import upickle.implicits.namedTuples.default.strict.given
@main def hello(): Unit =
println(msg)
val mytuple = ( name = "bob", messages = Seq(( verb = "ing", content = "bazinga" ), ( verb = "ing", content = "bazinga" )))
val json = upickle.default.write(mytuple)
println(json)
println("START "+stack.size)// Capture checking stack allocation
val poolAcceptClosure2 = withFreshPooled( pooled => {
println("PoolOne: " + pooled.toString())
withFreshPooled( pooled => {
pooled.toString }, "innerPool")
}, "outerPool") //; println("NEXT-AFTER: " + nextFree); val poolAcceptClosure = withFreshPooled( pooled => { pooled.toString }, "outerPool1" ); println(poolAcceptClosure)
println("PoolTwo: "+ poolAcceptClosure2)
println("END "+stack.size)
program[Id]// Run with Id (side effects)
val pretty: Pretty[Unit] = program[Pretty] // Run with Pretty (pure, accumulates output)
println(pretty)
def msg = "I was compiled by Scala 3. :)"
// Minimal Monad type class
trait Monad[F[_]]:
extension [A](fa: F[A])
def flatMap[B](f: A => F[B]): F[B]
def map[B](f: A => B): F[B]
// Define the capability (type class) for Console
trait Console[F[_]]:
def putStrLn(line: String): F[Unit]
def getStrLn: F[String]
// The tagless final program is abstract over F and requires a Console[F] capability
def program[F[_]: Monad](using Console[F]): F[Unit] =
for
_ <- summon[Console[F]].putStrLn("What is your name?")
name <- summon[Console[F]].getStrLn
_ <- summon[Console[F]].putStrLn(s"Hello, $name!")
yield ()
// Example interpreter
type Id[A] = A
given Console[Id] with
def putStrLn(line: String): Id[Unit] = println(line)
def getStrLn: Id[String] = scala.io.StdIn.readLine()
given Monad[Id] with
extension [A](fa: Id[A])
def flatMap[B](f: A => Id[B]): Id[B] = f(fa)
def map[B](f: A => B): Id[B] = f(fa)
// Example interpreter for accumulating output as a List[String]
// Pretty interpreter: accumulates a log of operations
case class Pretty[A](log: List[String], value: A)
given Monad[Pretty] with
extension [A](fa: Pretty[A])
def flatMap[B](f: A => Pretty[B]): Pretty[B] =
val Pretty(log1, a) = fa
val Pretty(log2, b) = f(a)
Pretty(log1 ++ log2, b)
def map[B](f: A => B): Pretty[B] =
Pretty(fa.log, f(fa.value))
given Console[Pretty] with
def putStrLn(line: String): Pretty[Unit] =
Pretty(List(s"putStrLn($line)"), ())
def getStrLn: Pretty[String] =
Pretty(List("getStrLn"), "<input>")