Programming Rule Oriented Formalized English Sentence Specifications
A Scala 3 framework for building domain-specific languages with English-like syntax and formal semantic foundations.
Author: Dr. Mark Grechanik β’ π§ Email 0x1DOCD00D
- Features
- Objectives
- Quick Start
- Syntax Specification
- Design Overview
- Formal Semantics
- Implementation
- Cats Effect Integration
- Code Examples
- Project Structure
- Development
- Contributing
- License
- π£οΈ Natural Syntax β Write specifications in English-like sentences
- π¦ First-Class Values β Expressions are assignable, passable, composable
- π Pluggable Semantics β Define your own interpreters and handlers
- π± Cats Effect Integration β Type-safe functional effect management
- π Formal Foundations β Operational and denotational semantics with proven properties
- β‘ Algebraic Optimizations β Provably correct transformations from monoid laws
- π§ Minimal Learning Curve β Domain experts can write specifications without learning Scala
Imagine you want to write this in your Scala program:
val trade = (broker Mark) sold 700 shares (stock MSFT) at 150 dollarsIn standard Scala, this would fail immediately. The compiler would complain:
brokeris not definedMarkis not definedsoldis not a methodsharesis not defined- ... and so on for every word
You would need to declare dozens of variables, methods, classes, and implicits just to make this compile. And if you wanted to change "sold" to "purchased" or add "on exchange NYSE", you'd need even more boilerplate.
PROFESS lets you write arbitrary English sentences as valid Scala code with:
| Feature | Benefit |
|---|---|
| No declarations needed | Write broker, Mark, sold without defining them first |
| Natural word order | Sentences flow like English, not like code |
| Deferred semantics | Define what words mean separately from how they look |
| Compile-time safety | Syntax errors caught during compilation |
| First-class values | Sentences are values you can pass, store, and compose |
When you write:
val order = (broker Jane) bought 500 (stock AAPL)PROFESS automatically:
- Recognizes patterns β
(broker Jane)is a PROFESS object with kindbrokerand nameJane - Generates scaffolding β Creates the necessary
broker,Jane,bought,stock,AAPLidentifiers - Builds an IR graph β Constructs a computation graph representing the sentence structure
- Enables interpretation β Lets you define what
boughtmeans in your domain
The result is a ProfessExpr containing:
IRSequence([
IRObject("broker", "Jane"),
IRWord("bought"),
IRNumber(500),
IRObject("stock", "AAPL")
])
The key innovation is separation of syntax from semantics. You write sentences first, then define their meaning:
// Write the sentence (syntax)
val order = (broker Jane) bought 500 (stock AAPL)
// Later, define what "bought" means (semantics)
val handlers = HandlerDSL.handlers[IO, Trade]
.onWord("bought") { (args, _) =>
val qty = args.collectFirst { case IRNumber(n) => n.toInt }.getOrElse(0)
IO.pure(Trade(action = "buy", quantity = qty))
}
.onObject("broker") { (name, _) => IO.pure(Trade(broker = name)) }
.onObject("stock") { (name, _) => IO.pure(Trade(symbol = name)) }
.build
// Execute with your semantics
val result: IO[Trade] = runWithHandlers(order)This means:
- Domain experts write specifications in natural language
- Developers implement the interpretation logic separately
- The same sentence can have different meanings in different contexts
| Traditional DSL | PROFESS |
|---|---|
| Define all keywords upfront | Use any words you want |
| Rigid syntax rules | Flexible English-like flow |
| Syntax and semantics coupled | Syntax and semantics separated |
| Hard to extend | Add new words anytime |
| Requires Scala expertise | Domain experts can contribute |
A PROFESS expression is not just text β it's a graph of computation:
- Nodes are instances of PROFESS types (
IRObject,IRWord,IRNumber, etc.) - Edges are method calls that return values of these types
(broker Mark) sold 700 (stock MSFT)
β β β β
βΌ β β βΌ
IRObject ββββββΌββββΌββ IRObject
(broker, β β (stock,
"Mark") β β "MSFT")
βΌ βΌ
IRWord IRNumber
(sold) (700)
β
βΌ
IRSequence([...])
This graph structure enables powerful transformations through partial evaluation and Futamura projections.
Partial evaluation specializes a program with respect to part of its input. When applied to a DSL interpreter, it produces a residual program in an intermediate representation (IR) that no longer requires interpretation of the original DSL code.
Given:
Iβ an interpreter for the DSLPβ a DSL programPEβ a partial evaluator
Then: IR_P = PE(I, P) β the partial evaluator specializes the interpreter with respect to P, generating a residual program IR_P.
The first Futamura projection specializes an interpreter for a specific DSL program, effectively compiling it into optimized code.
// DSL for arithmetic expressions
sealed trait Expr
case class Const(value: Int) extends Expr
case class Var(name: String) extends Expr
case class Add(left: Expr, right: Expr) extends Expr
// Interpreter
def interpret(expr: Expr, env: Map[String, Int]): Int = expr match {
case Const(v) => v
case Var(n) => env.getOrElse(n, 0)
case Add(l, r) => interpret(l, env) + interpret(r, env)
}
// Partial evaluator (first Futamura projection)
def partialEval(expr: Expr): Map[String, Int] => Int = {
(env: Map[String, Int]) => interpret(expr, env)
}
// Specialize for a specific program: Add(Var("x"), Const(3))
val specialized = partialEval(Add(Var("x"), Const(3)))
// specialized is now equivalent to: (env) => env("x") + 3
specialized(Map("x" -> 2)) // Returns 5The result specialized is a function x + 3 with the interpretation overhead eliminated.
The second Futamura projection takes specialization further β it specializes the partial evaluator with respect to the interpreter itself, producing a compiler for the DSL:
// Partial evaluator that takes an interpreter as input
def partialEval(interpret: (Expr, Map[String, Int]) => Int):
Expr => (Map[String, Int] => Int) = {
(expr: Expr) => (env: Map[String, Int]) => interpret(expr, env)
}
// Generate a compiler by fixing the interpreter
val compiler = partialEval(interpret)
// Use the compiler on any program
val compiled = compiler(Add(Var("x"), Const(3)))
compiled(Map("x" -> 4)) // Returns 7The compiler can now take any DSL program and compile it to efficient host language code.
The third Futamura projection specializes the partial evaluator to itself, yielding a compiler generator β a reusable function that can generate compilers for many different DSL interpreters:
// Multiple intermediate representations
sealed trait SuperIR
sealed trait IRExpr1 extends SuperIR
case class IRConst1(value: Int) extends IRExpr1
case class IRVar1(name: String) extends IRExpr1
case class IRAdd1(left: IRExpr1, right: IRExpr1) extends IRExpr1
sealed trait IRExpr2 extends SuperIR
case class IRNumber(value: Int) extends IRExpr2
case class IRNamed(name: String) extends IRExpr2
case class IRSum(left: IRExpr2, right: IRExpr2) extends IRExpr2
// Translators to each IR
def toIR1(expr: Expr): IRExpr1 = expr match {
case Const(v) => IRConst1(v)
case Var(n) => IRVar1(n)
case Add(l, r) => IRAdd1(toIR1(l), toIR1(r))
}
def toIR2(expr: Expr): IRExpr2 = expr match {
case Const(v) => IRNumber(v)
case Var(n) => IRNamed(n)
case Add(l, r) => IRSum(toIR2(l), toIR2(r))
}
// Evaluators for each IR (with different semantics!)
def evalIR1(ir: IRExpr1, env: Map[String, Int]): Int = ir match {
case IRConst1(v) => v
case IRVar1(name) => env.getOrElse(name, 0)
case IRAdd1(l, r) => evalIR1(l, env) + evalIR1(r, env)
}
def evalIR2(ir: IRExpr2, env: Map[String, Int]): Int = ir match {
case IRNumber(v) => v + 1 // Different semantics!
case IRNamed(name)=> env.getOrElse(name, 1)
case IRSum(l, r) => evalIR2(l, env) * evalIR2(r, env) // Multiplication!
}
// Compiler generator (third Futamura projection)
def compilerGenerator[I <: SuperIR](
toIR: Expr => I,
eval: (I, Map[String, Int]) => Int
): Expr => Map[String, Int] => Int = {
(expr: Expr) => (env: Map[String, Int]) => eval(toIR(expr), env)
}
// Generate different compilers from the same generator
val compiler1 = compilerGenerator(toIR1, evalIR1)
val compiler2 = compilerGenerator(toIR2, evalIR2)
val program = Add(Var("x"), Const(3))
compiler1(program)(Map("x" -> 5)) // Standard: 5 + 3 = 8
compiler2(program)(Map("x" -> 5)) // Different: (5+1) * (3+1) = 24PROFESS leverages the third Futamura projection to achieve powerful flexibility:
| Projection | PROFESS Application |
|---|---|
| 1st | Specialize a handler registry for a specific PROFESS expression |
| 2nd | Generate domain-specific compilers from handler definitions |
| 3rd | Create compiler generators that target multiple backends |
This enables:
-
Multiple IRs β The same PROFESS sentence can compile to different intermediate representations (monolithic app, distributed system, actor model, etc.)
-
Pluggable Backends β Swap interpreter-evaluator pairs to target Akka, Spark, or custom runtimes
-
Composable Compilation β Combine partial evaluators to build sophisticated compilation pipelines
// Same PROFESS syntax, different compilation targets
val trade = (broker Mark) sold 700 (stock MSFT)
// Compile to local execution
val localCompiler = compilerGenerator(toLocalIR, evalLocal)
val localResult = localCompiler(trade)(context)
// Compile to distributed Akka actors
val akkaCompiler = compilerGenerator(toAkkaIR, evalAkka)
val akkaResult = akkaCompiler(trade)(context)
// Compile to Spark jobs
val sparkCompiler = compilerGenerator(toSparkIR, evalSpark)
val sparkResult = sparkCompiler(trade)(context)| Concept | Description |
|---|---|
| PROFESS Expression | A computation graph with typed nodes and method edges |
| Partial Evaluation | Specializing interpreters to eliminate runtime overhead |
| 1st Projection | Expression β Specialized Program |
| 2nd Projection | Interpreter β Compiler |
| 3rd Projection | Partial Evaluator β Compiler Generator |
| PROFESS Benefit | Same syntax compiles to multiple targets via pluggable IR/evaluator pairs |
PROFESS is designed to bridge the gap between natural language specifications and executable Scala code:
-
Enable Domain Experts to Write Specifications β Allow business analysts and domain experts to write specifications in English-like syntax that compiles directly to working Scala programs.
-
First-Class Expression Values β Every PROFESS expression evaluates to a first-class value that can be assigned, passed, returned, and composed.
-
Domain-Agnostic Framework β No predefined vocabulary. Semantics are defined externally through interpreter functions.
-
Formal Foundation β Rigorous operational and denotational semantics ensuring predictable behavior.
-
Functional Effect Integration β Full integration with Typelevel Cats and Cats Effect for type-safe effect management.
Add to your build.sbt:
libraryDependencies += "io.github.yourusername" %% "profess-runtime" % "0.1.0"
// For compiler plugin (enables natural syntax)
addCompilerPlugin("io.github.yourusername" %% "profess-plugin" % "0.1.0")import profess.runtime._
// Define expressions with natural syntax
val trade = (broker Mark) sold 700 shares (stock MSFT) at 150 dollars
// Variable interpolation with !
val name = "Jane"
val qty = 500
val order = (broker !name) bought !qty (stock AAPL)
// Unit values with :
val precise = (broker Mark) sold 700:shares at 150.50:dollars
// First-class values - pass to functions
def analyze(expr: ProfessExpr): Report = ???
val report = analyze(trade)import cats.effect.IO
import profess.runtime.cats._
// Define handlers for your domain
given DomainHandlers[IO, Trade] with
val registry = HandlerDSL.handlers[IO, Trade]
.onObject("broker") { (name, _) => IO.pure(Trade(broker = name)) }
.onWord("sold") { (args, _) =>
val qty = args.collectFirst { case IRNumber(n) => n.toInt }.getOrElse(0)
IO.pure(Trade(action = "sell", quantity = qty))
}
.build
// Execute with automatic handler injection
val result: IO[Trade] = runWithHandlers[IO, Trade](trade)expression ::= subject parts
subject ::= PROFESS-object | word | variable-ref
PROFESS-object ::= '(' kindId (name | '!' variable) ')'
variable-ref ::= '!' variable
parts ::= (word | PROFESS-object | number | variable-ref | unit-value)*
unit-value ::= number ':' word
conditional ::= 'If' expression 'then' expression ('else' | 'otherwise') expression| Element | Syntax | Example |
|---|---|---|
| PROFESS Object | (kindId Name) |
(broker Mark) |
| Variable Interpolation | !variable |
(broker !name) |
| Unit Value | number:unit |
700:shares |
| Conditional | If...then...else |
If online then allow else deny |
| Sequence | juxtaposition | sold 700 shares |
Functional Requirements:
- Expression syntax with PROFESS objects
(kindId Name)where kindId is lowercase - Variable interpolation with
!prefix operator - Unit values with
:operator for attaching units (e.g.,700:shares,150:dollars) - Conditional expressions with
If...then...else/otherwiseconstructs - Scope-aware scaffolding that only scaffolds truly undeclared identifiers
Non-Functional Requirements:
- Minimal compilation overhead
- Scala 3.4+ compatibility
- Extensibility through user-defined interpreters and handlers
PROFESS consists of three main components:
-
Compiler Plugin β Runs between parser and typer phases. Identifies patterns and generates scaffolding for undeclared identifiers.
-
Runtime Library β Provides
ProfessExpr, IR types, scaffolding types, and theDynamictrait implementation. -
Cats Integration β Handler typeclasses, registry, traverser, and effect execution.
| Stage | Description |
|---|---|
| 1. Parser | Scala parser produces untyped AST |
| 2. PROFESS Plugin | Collect declarations, identify patterns, generate scaffolding |
| 3. Typer | Type checking with scaffolded identifiers |
| 4. Execution | Expressions evaluate to ProfessExpr values |
| 5. Interpretation | Handlers transform IR nodes into domain objects or effects |
Source Code After Plugin At Runtime
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
(broker Mark) sold 700 β broker.apply(Mark) β ProfessExpr(
.selectDynamic("sold") IRSequence([
.applyDynamic(700) IRObject("broker","Mark"),
IRWord("sold"),
IRNumber(700)
])
)
PROFESS has rigorous formal foundations based on operational and denotational semantics.
e β Expr Expressions
v β Val Values
n β Num Numeric literals
w β Word Words (identifiers)
k β Kind Kind identifiers (lowercase)
x β Var Variables
ΞΉ β IR Intermediate Representation nodes
e ::= (k n) -- PROFESS object
| (k !x) -- Object with variable interpolation
| !x -- Variable interpolation
| n | n:w | w -- Literals and words
| e e -- Sequence (juxtaposition)
| If e then e else e -- Conditional
We define the judgment Ο β’ e β v meaning "under environment Ο, expression e evaluates to value v":
Rule [E-Object]: PROFESS Object Evaluation
βββββββββββββββββββββββββββββββββββββββββββββββββ
Ο β’ (k n) β ProfessExpr(IRObject(k, n))
Rule [E-Var]: Variable Interpolation
Ο(x) = v
ββββββββββββββ
Ο β’ !x β v
Rule [E-Seq]: Sequence Evaluation
Ο β’ eβ β ProfessExpr(ΞΉβ) Ο β’ eβ β ProfessExpr(ΞΉβ)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Ο β’ eβ eβ β ProfessExpr(flatten(ΞΉβ, ΞΉβ))
Rule [E-Cond]: Conditional Evaluation
Ο β’ eβ β ProfessExpr(ΞΉβ) Ο β’ eβ β ProfessExpr(ΞΉβ) Ο β’ eβ β ProfessExpr(ΞΉβ)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Ο β’ If eβ then eβ else eβ β ProfessExpr(IRConditional(ΞΉβ, ΞΉβ, ΞΉβ))
The semantic function β¦_β§ : Expr β Env β Val maps expressions to values:
β¦(k n)β§Ο = ProfessExpr(IRObject(k, n))
β¦!xβ§Ο = Ο(x)
β¦eβ eββ§Ο = let ProfessExpr(ΞΉβ) = β¦eββ§Ο in
let ProfessExpr(ΞΉβ) = β¦eββ§Ο in
ProfessExpr(flatten(ΞΉβ, ΞΉβ))
β¦If eβ then eβ else eββ§Ο =
ProfessExpr(IRConditional(β¦eββ§Ο.ir, β¦eββ§Ο.ir, β¦eββ§Ο.ir))
Handlers form a monoid (H, β, Ξ΅) under composition:
Identity: R β Ξ΅ = Ξ΅ β R = R
Associativity: (Rβ β Rβ) β Rβ = Rβ β (Rβ β Rβ)
Handler lookup with precedence (later handlers override):
lookup(w, Rβ β Rβ) = Rβ(w) if w β dom(Rβ)
= Rβ(w) if w β dom(Rβ) β§ w β dom(Rβ)
= β₯ otherwise
Given registry R and result monoid (A, β, Ξ΅_A), interpretation is:
interp : IR Γ Registry Γ Context β F[A]
interp(IRObject(k, n), R, ctx) =
case R.objects(k) of
Some(h) β h.handle(n, ctx)
None β pure(Ξ΅_A)
interp(IRSequence(ΞΉ :: rest), R, ctx) =
interp(ΞΉ, R, ctx) β_F interp(IRSequence(rest), R, ctx)
Where β_F lifts the monoid into the effect:
mβ β_F mβ = for { aβ β mβ; aβ β mβ } yield aβ β aβ
| Property | Statement |
|---|---|
| Determinism | Evaluation produces unique results |
| Type Soundness (Progress) | If β
β’ e : Ο then either e is a value or βe'. e β e' |
| Type Soundness (Preservation) | If Ξ β’ e : Ο and e β e' then Ξ β’ e' : Ο |
| Denotational-Operational Correspondence | β¦eβ§Ο = v iff Ο β’ e β v |
| Handler Compositionality | For any Rβ, Rβ: interp(e, Rβ β Rβ) is well-defined |
| Effect Safety | For pure handlers, interpretation is referentially transparent |
| Algebraic Law | Optimization |
|---|---|
R β Ξ΅ = R |
Dead handler elimination |
| Associativity | Handler fusion (single-pass compilation) |
| Commutativity | Reordering for locality |
The Intermediate Representation captures the structure of PROFESS expressions:
sealed trait IRNode
case class IRObject(kind: String, name: String) extends IRNode
case class IRWord(word: String) extends IRNode
case class IRNumber(value: Double) extends IRNode
case class IRString(value: String) extends IRNode
case class IRUnitValue(value: Double, unit: String) extends IRNode
case class IRSequence(nodes: List[IRNode]) extends IRNode
case class IRConditional(
condition: IRNode,
thenBranch: IRNode,
elseBranch: Option[IRNode]
) extends IRNode
case class IRBinding(variable: String, value: IRNode) extends IRNode
case class IRReference(variable: String) extends IRNodeThese types enable the natural syntax:
// Kind identifier - lowercase, used as method name
class ProfessKind(val kind: String) extends Dynamic:
def apply(name: ProfessName): ProfessExpr = ...
def apply(ref: ProfessRef): ProfessExpr = ...
// Name identifier - capitalized entity name
class ProfessName(val name: String)
// Word - action or modifier in expression
class ProfessWord(val word: String)
// Object wrapper for building expressions
class ProfessObject(val kind: String, val name: String)The core expression type:
class ProfessExpr(nodes: List[IRNode]) extends Dynamic:
def toIR: IRNode = nodes match
case single :: Nil => single
case multiple => IRSequence(multiple)
// Enable method chaining: expr.sold
def selectDynamic(word: String): ProfessExpr =
ProfessExpr(nodes :+ IRWord(word))
// Enable method calls: expr.sold(700)
def applyDynamic(word: String)(args: Any*): ProfessExpr =
val wordNode = IRWord(word)
val argNodes = args.map(convertToIR).toList
ProfessExpr(nodes ++ (wordNode :: argNodes))
// Combine expressions
def combine(other: ProfessExpr): ProfessExpr =
ProfessExpr(this.nodes ++ other.nodes)trait WordHandler[F[_], A]:
def word: String
def handle(args: List[IRNode], ctx: TransformContext): F[A]
trait ObjectHandler[F[_], A]:
def kind: String
def handle(name: String, ctx: TransformContext): F[A]class HandlerRegistry[F[_], A](
wordHandlers: Map[String, WordHandler[F, A]],
objectHandlers: Map[String, ObjectHandler[F, A]]
):
def findWordHandler(word: String): Option[WordHandler[F, A]]
def findObjectHandler(kind: String): Option[ObjectHandler[F, A]]
// Monoid composition
def combine(other: HandlerRegistry[F, A]): HandlerRegistry[F, A]Fluent builder for creating registries:
val registry = HandlerDSL.handlers[IO, Trade]
.onWord("sold") { (args, ctx) =>
val qty = args.collectFirst { case IRNumber(n) => n.toInt }.getOrElse(0)
IO.pure(Trade(action = "sell", quantity = qty))
}
.onWord("bought") { (args, ctx) =>
val qty = args.collectFirst { case IRNumber(n) => n.toInt }.getOrElse(0)
IO.pure(Trade(action = "buy", quantity = qty))
}
.onObject("broker") { (name, ctx) =>
IO.pure(Trade(broker = name))
}
.onObject("stock") { (name, ctx) =>
IO.pure(Trade(symbol = name))
}
.build// Define handlers as a given instance
given DomainHandlers[IO, Trade] with
val registry = HandlerDSL.handlers[IO, Trade]
.onWord("sold") { (args, _) => IO.pure(Trade(action = "sell")) }
.onObject("broker") { (name, _) => IO.pure(Trade(broker = name)) }
.build
// Handlers automatically injected via context
val trade = (broker Mark) sold 700 (stock MSFT)
val result: IO[Trade] = runWithHandlers[IO, Trade](trade)class IRTraverser[F[_]: Monad, A: Monoid](registry: HandlerRegistry[F, A]):
def traverse(expr: ProfessExpr): F[A] =
traverseNode(expr.toIR)
private def traverseNode(node: IRNode): F[A] = node match
case IRObject(kind, name) =>
registry.findObjectHandler(kind) match
case Some(handler) => handler.handle(name, ctx)
case None => Monoid[A].empty.pure[F]
case IRWord(word) =>
registry.findWordHandler(word) match
case Some(handler) => handler.handle(Nil, ctx)
case None => Monoid[A].empty.pure[F]
case IRSequence(nodes) =>
nodes.traverse(traverseNode).map(_.combineAll)
case IRConditional(cond, thenB, elseB) =>
for
c <- traverseNode(cond)
t <- traverseNode(thenB)
e <- elseB.traverse(traverseNode).map(_.getOrElse(Monoid[A].empty))
yield c |+| t |+| e
case _ => Monoid[A].empty.pure[F]val trade1 = (broker Mark) sold 700 shares (stock MSFT)
val trade2 = (broker Jane) bought 500 (stock AAPL)val name = "Mark"
val qty = 700
val ticker = "MSFT"
val trade = (broker !name) sold !qty (stock !ticker)val order = (broker Mark) sold 700:shares at 150.50:dollarsval review = If trade exceeds 1000 then flag for review otherwise auto approve
val access = If user is authenticated then grant access else denyimport cats._
import cats.effect._
import profess.runtime._
import profess.runtime.cats._
// Domain model
case class Trade(
broker: String = "",
action: String = "",
quantity: Int = 0,
symbol: String = "",
price: Double = 0.0
)
// Monoid for combining partial trades
given Monoid[Trade] with
def empty = Trade()
def combine(x: Trade, y: Trade) = Trade(
broker = if y.broker.nonEmpty then y.broker else x.broker,
action = if y.action.nonEmpty then y.action else x.action,
quantity = if y.quantity > 0 then y.quantity else x.quantity,
symbol = if y.symbol.nonEmpty then y.symbol else x.symbol,
price = if y.price > 0 then y.price else x.price
)
// Handler definitions
given DomainHandlers[IO, Trade] with
val registry = HandlerDSL.handlers[IO, Trade]
.onObject("broker") { (name, _) =>
IO.pure(Trade(broker = name))
}
.onObject("stock") { (name, _) =>
IO.pure(Trade(symbol = name))
}
.onWord("sold") { (args, _) =>
val qty = args.collectFirst { case IRNumber(n) => n.toInt }.getOrElse(0)
IO.pure(Trade(action = "sell", quantity = qty))
}
.onWord("bought") { (args, _) =>
val qty = args.collectFirst { case IRNumber(n) => n.toInt }.getOrElse(0)
IO.pure(Trade(action = "buy", quantity = qty))
}
.onWord("at") { (args, _) =>
val price = args.collectFirst { case IRNumber(n) => n }.getOrElse(0.0)
IO.pure(Trade(price = price))
}
.build
// Usage
val trade = (broker Mark) sold 700 (stock MSFT) at 150.50
val result: IO[Trade] = runWithHandlers[IO, Trade](trade)
// result.unsafeRunSync() == Trade("Mark", "sell", 700, "MSFT", 150.50)The formal theory directly supports process calculus translation:
// Ο-calculus process definition
val pingPong =
new (channel ping) in
new (channel pong) in
(
(channel ping) receives x then (channel pong) sends x then stop
) parallel (
(channel pong) receives y then (channel ping) sends y then stop
) parallel (
(channel ping) sends "hello" then stop
)
// Translates to Akka actors with semantic preservation
val akkaCode = PiTranslator.translate(pingPong)profess/
βββ plugin/ # Scala 3 compiler plugin
β βββ src/main/scala/profess/plugin/
β βββ ProfessPlugin.scala # AST transformation
βββ runtime/ # Core runtime library
β βββ src/main/scala/profess/runtime/
β βββ Runtime.scala # IR types, ProfessExpr
β βββ cats/
β βββ CatsInterpreter.scala # Cats Effect integration
βββ examples/ # Usage examples
β βββ src/main/scala/examples/
β βββ BasicExamples.scala
β βββ CatsExamples.scala
β βββ PiCalculusExample.scala
βββ docs/ # Documentation
β βββ DESIGN.md
β βββ FORMAL-SEMANTICS.md
β βββ APPLIED-THEORY.md
βββ build.sbt
βββ LICENSE # Apache 2.0
βββ README.md
| Document | Description |
|---|---|
| Design | Architecture and implementation details |
| Formal Semantics | Operational/denotational semantics, type system |
| Applied Theory | Optimizations, Ο-calculus example |
| GitHub Setup | How to set up this project |
- JDK 17 or 21 (LTS)
- sbt 1.10+
- Scala 3.6+
sbt compile # Compile all modules
sbt test # Run tests
sbt fmt # Format code (alias for scalafmtAll)
sbt fmtCheck # Check formatting
sbt runExamples # Run example code
sbt coverage test # Run tests with coverage- Install IntelliJ IDEA with Scala plugin
- Open project: File β Open β select profess directory
- Import as sbt project when prompted
- Wait for indexing to complete
# All tests
sbt test
# Specific module
sbt "runtime/test"
# With coverage report
sbt coverage test coverageReportContributions welcome! Please see CONTRIBUTING.md.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Author: Dr. Mark Grechanik
- Email: 0x1DOCD00D
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Author & Lead Developer: Prof. Mark Grechanik β University of Illinois, Chicago and Lone Star Consulting, Inc.
Apache License 2.0 - see LICENSE for details.
PROFESS β Bridging Natural Language and Executable Code
Made with β€οΈ for the Scala community