Airframe

Airframe

  • Docs
  • Blog
  • Release Notes
  • GitHub

›Core Modules

Resources

  • Overview
  • Articles
  • Release Notes
  • Logos

Framework

  • airframe-di: Dependency Injection
  • Airframe RPC
  • airframe-http: Creating REST Service
  • airframe-rx: ReactiveX interface
  • AirSpec: Testing Framework

Core Modules

  • airframe-codec: Schema-On-Read Object Serializer
  • airframe-config: Application Config Flow
  • airframe-control: Retry/Rate Control
  • airframe-log: Application Logger
  • airframe-metrics: Human-Friendly Measures for Time and Data Size
  • airframe-surface: Object Shape Inspector

Utilities

  • airframe-benchmark: JMH Benchmark
  • airframe-canvas: Off-Heap Memory Manager
  • airframe-fluentd: Fluentd Logger
  • airframe-http-recorder: Web Request/Response Recorder
  • airframe-jdbc: JDBC Connection Pool
  • airframe-jmx: JMX Application Monitor
  • airframe-json: Pure-Scala JSON Parser
  • airframe-launcher: Command-Line Program Launcher
  • airframe-msgpack: Pure-Scala MessagePack Parser
  • airframe-parquet: Parquet Columnar File Reader and Writer
  • airframe-sql: SQL Parser
  • airframe-ulid: ULID Generator

airframe-surface: Object Shape Inspector

airframe-surface enables reading type information, such as:

  • Constructor parameter names and their types
  • Methods defined in an object with method argument names and types.

Usage

Maven Central Scaladoc

build.sbt

libraryDependencies += "org.wvlet.airframe" %% "airframe-surface" % "(version)"

// For Scala.js
libraryDependencies += "org.wvlet.airframe" %%% "airframe-surface" % "(version)"
  • Surface.of[X] returns parameters,
  • MethodSurface.of[X] returns the list of methods in X and their parameter names and types.
  • airframe-surface is compatible between Scala and Scala.js

Applications of Surface include:

  • Writing object serializer / deserializer without any boilerplates.
    • For example, airframe-codec Automatically generates code serialiation and deserizliation using Surface.
  • Dependency injection based on object shapes (e.g., Airframe DI)
    • airframe-di detects dependent types using Surface
  • Surface can be an indentifer of object types and type aliases.

Initial design note: Surface Design Document

Surface.of[X]

import wvlet.airframe.surface.Surface

case class A(id:Int, name:String)

val s = Surface.of[A]
println(s.toString) // This will show A(id:Int, name:String)

// Find object parameters
s.params.mkString(", ") // Returns "id:Int, name:String"

// Object factory
s.objectFactory.map{ f =>
  f.newInstance(Seq(1, "leo"))
}
// Some(A(1, "leo"))

Surface.methodsOf[X]

Extract the public method definitions of a given class:

class A {
  def hello(name:String): String = s"hello ${name}"
}

val methods = Surface.methodsOf[A]
val h = methods.head
h.name // "hello"
h.returnType // Surface.of[String]
h.args(0).surface // Surface.of[String]

Type alias

type UserName = String

Surface.of[UserName] //  Returns UserName:=String

In Scala 3, opaque type aliases are also supported:

opaque type UserName = String
Surface.of[UserName] // Returns UserName:=String

Warning: In Scala 3, due to the eager type alias expansion https://github.com/wvlet/airframe/issues/2200, the surface of a type alias can be the same surface of the referenced type. If you need to differentiate the same type with different names, use intersection types.

type UserName = String

// In Scala 3, this function can be resolved as String => Int:
val f1 = { (x: UserName) => x.toString.length }

// To resolve this as UserName => Int, explicitly specify the function type:
val f2: Funciton1[UserName, Int] = { (x: UserName) => x.toString.length }

Intersection Types

In Scala3, you can use intersection types for labeling types.

For example, you can use String & Environment and String & Stage for differentiating the same type for different purposes:

import wvlet.airframe.surface.Surface

trait Environment
trait Stage

Surface.of[String & Environment]
Surface.of[String & Stage]

Union type A | B is also supported:

import wvlet.airframe.surface.Surface

Surface.of[String | Int]

Tagged Types

To have different surfaces for the same type, you can use tagged type (@@):

import wvlet.airframe.surface.Surface
import wvlet.airframe.surface.tag._

class Fruit
trait Apple
trait Banana

Surface.of[Fruit @@ Apple]
Surface.of[Fruit @@ Banana]

Runtime Annotation

Reading runtime-annotation is supported for JVM projects. Import wvlet.airframe.surface.reflect._ to use this feature.

import wvlet.airframe.surface.Surface
import wvlet.airframe.surface.reflect._
import javax.annotation.Resource
 
@Resource(name="my resource")
class A(@Resource(name = "param 1") a:String)

val s = Surface.of[A]
// Reading class annotation
val a = s.findAnnotationOf[Resource]
a.get.name // "my resource"

// Reading parameter annotation
val r = s.params.find(_.name == "a").findAnnotationOf[Resource]
r.get.name // "param 1"

← airframe-metrics: Human-Friendly Measures for Time and Data Sizeairframe-benchmark: JMH Benchmark →
  • Usage
  • Surface.of[X]
  • Surface.methodsOf[X]
    • Type alias
    • Intersection Types
    • Tagged Types
    • Runtime Annotation
Airframe
Docs
Documentation
Community
Gitter Chat
More
GitHubStar
airframe logo
Copyright © 2024 wvlet.org