Slide 1

Slide 1 text

Mohit Sarveiya Writing Compiler Plugins with Arrow Meta www.twitter.com/heyitsmohit www.codingwithmohit.com

Slide 2

Slide 2 text

Writing Compiler Plugins with Arrow Meta ● Kotlin Compiler ● Compiler Plugin with Meta ● Testing Compiler Plugins ● IDE Plugins

Slide 3

Slide 3 text

Motivations ● How do we build tools with Kotlin? ● How do we introduce Functional Programming?

Slide 4

Slide 4 text

Code Parsing Analysis Resolve Code-gen Done class UserRepo { fun getUsers(): List fun addUser() fun deleteUser(id: Int) }

Slide 5

Slide 5 text

Code Parsing Analysis Resolve Code-gen Done Abstract Syntax Tree (AST)

Slide 6

Slide 6 text

Code Parsing Analysis Resolve Code-gen Done Semantics

Slide 7

Slide 7 text

Code Parsing Analysis Resolve Code-gen Done Type Information

Slide 8

Slide 8 text

Code Parsing Analysis Resolve Code-gen Done Type Checking

Slide 9

Slide 9 text

Code Parsing Analysis Resolve Code-gen Done class UserRepo { fun getUsers(): List fun addUser() fun deleteUser(id: Int) } Type Checking

Slide 10

Slide 10 text

Code Parsing Analysis Resolve Code-gen Done if ("hello" "== 1) { } Operator ‘"==’cannot be applied to ‘String’ and ‘Int’ Type Checking

Slide 11

Slide 11 text

Code Parsing Analysis Resolve Code-gen Done Intermediate Representation (IR) Js JVM Native

Slide 12

Slide 12 text

Code Parsing Analysis Resolve Code-gen Done Compiled!

Slide 13

Slide 13 text

ARROW META

Slide 14

Slide 14 text

arrow-kt/arrow-meta Code ! Issues Pull Requests rrow Meta Writing compiler plugins, source transformations, IDEA plugins, linters, type search engines, automatic code refactoring,""... Gradle Build passing latest snapshot v1.3.61

Slide 15

Slide 15 text

Code Parsing Analysis Resolve Code-gen Done

Slide 16

Slide 16 text

Code Parsing Analysis Resolve Code-gen Done class UserRepo { fun getUsers(): List fun addUser() fun deleteUser(id: Int) }

Slide 17

Slide 17 text

Code Parsing Analysis Resolve Code-gen Done Abstract Syntax Tree (AST)

Slide 18

Slide 18 text

Code Parsing Analysis Resolve Code-gen Done Quote Templates Search and transform

Slide 19

Slide 19 text

Code Parsing Analysis Resolve Code-gen Done Type Checking

Slide 20

Slide 20 text

Code Parsing Analysis Resolve Code-gen Done Type Checker

Slide 21

Slide 21 text

Code Parsing Analysis Resolve Code-gen Done Type Checker Custom Type Checker

Slide 22

Slide 22 text

Code Parsing Analysis Resolve Code-gen Done Intermediate Representation (IR) Js JVM Native Hook into IR

Slide 23

Slide 23 text

Code Parsing Analysis Resolve Code-gen Done Compiled!

Slide 24

Slide 24 text

Compiler Plugins Write & Test

Slide 25

Slide 25 text

Writing Complier Plugins with Arrow Meta - Simple Hello World Plugin - Write & Test Plugin for logging - Combine Transformations - Custom Type Checker

Slide 26

Slide 26 text

Steps to Write Compiler Plugin 1. Create Compiler Plugin(s) 2. Register Plugin

Slide 27

Slide 27 text

fun main() { helloWorld() } fun helloWorld() { } HelloWorldKt src main kotlin com learn arrow-meta HelloWorldKt

Slide 28

Slide 28 text

fun main() { helloWorld() } fun helloWorld() { println("Hello DroidCon EMEA!”) } Generate HelloWorldKt src main kotlin com learn arrow-meta HelloWorldKt

Slide 29

Slide 29 text

val Meta.helloWorld: CliPlugin Entry Point for compiler

Slide 30

Slide 30 text

val Meta.helloWorld: CliPlugin Type alias

Slide 31

Slide 31 text

val Meta.helloWorld: CliPlugin typealias CliPlugin = Plugin

Slide 32

Slide 32 text

val Meta.helloWorld: CliPlugin data class Plugin( val name: String, val meta: A.() "-> List ) Name of compiler plugin

Slide 33

Slide 33 text

val Meta.helloWorld: CliPlugin data class Plugin( val name: String, val meta: A.() "-> List ) Phases of compilation

Slide 34

Slide 34 text

val Meta.helloWorld: CliPlugin get() = "Hello World" { } Creates a Plugin object

Slide 35

Slide 35 text

val Meta.helloWorld: CliPlugin get() = "Hello World" { } operator fun String.invoke( phases: CompilerContext.() "-> List ): CliPlugin = Plugin(this, phases)

Slide 36

Slide 36 text

val Meta.helloWorld: CliPlugin get() = "Hello World" { } How do we hook into the parsing phase?

Slide 37

Slide 37 text

val Meta.helloWorld: CliPlugin get() = "Hello World" { meta( ) } Intercept compilation phases

Slide 38

Slide 38 text

val Meta.helloWorld: CliPlugin get() = "Hello World" { meta( ) } fun meta( vararg phases: ExtensionPhase ): List Define list of phases

Slide 39

Slide 39 text

val Meta.helloWorld: CliPlugin get() = "Hello World" { meta( namedFunction(this, { name "== "helloWorld" }) { c "-> } ) } Finds a function named “helloWorld"

Slide 40

Slide 40 text

val Meta.helloWorld: CliPlugin get() = "Hello World" { meta( namedFunction(this, { name "== "helloWorld" }) { c "-> } ) } Quote Template DSL fun nodeName { } Function, Expression

Slide 41

Slide 41 text

val Meta.helloWorld: CliPlugin get() = "Hello World" { meta( namedFunction(this, { name "== "helloWorld" }) { c "-> } ) } Quote Template DSL fun nodeName { } Specify Transformation

Slide 42

Slide 42 text

val Meta.helloWorld: CliPlugin get() = "Hello World" { meta( namedFunction(this, { name "== "helloWorld" }) { c "-> } ) } Function

Slide 43

Slide 43 text

val Meta.helloWorld: CliPlugin get() = “Hello World" { meta( namedFunction(this, { name "== "helloWorld" }) { c "-> Transform.replace( ) } ) } Replace Transformation

Slide 44

Slide 44 text

val Meta.helloWorld: CliPlugin get() = “Hello World" { meta( namedFunction(this, { name "== "helloWorld" }) { c "-> Transform.replace( replacing = c newDeclaration = """|fun helloWorld(): Unit = | println("Hello DroidCon EMEA!") |""".function.syntheticScope ) } ) } Function to replace

Slide 45

Slide 45 text

val Meta.helloWorld: CliPlugin get() = “Hello World" { meta( namedFunction(this, { name "== "helloWorld" }) { c "-> Transform.replace( replacing = c, newDeclaration = """|fun helloWorld(): Unit = | println("Hello DroidCon EMEA!") |""".function.syntheticScope ) } ) } Replacement

Slide 46

Slide 46 text

val Meta.helloWorld: CliPlugin get() = “Hello World" { meta( namedFunction(this, { name "== "helloWorld" }) { c "-> Transform.replace( replacing = c, newDeclaration = """|fun helloWorld(): Unit = | println(“Hello DroidCon EMEA!") |""".function.syntheticScope ) } ) }

Slide 47

Slide 47 text

Writing Complier Plugins with Arrow Meta 1. Create Compiler Plugin(s) 2. Register Plugin

Slide 48

Slide 48 text

class MetaPlugin : Meta { override fun intercept(ctx: CompilerContext): List = listOf( helloWorld ) } Create Meta Class

Slide 49

Slide 49 text

class MetaPlugin : Meta { override fun intercept(ctx: CompilerContext): List = listOf( helloWorld ) } Register All Plugins with Kotlin Compiler

Slide 50

Slide 50 text

class MetaPlugin : Meta { override fun intercept(ctx: CompilerContext): List = listOf( helloWorld ) } Register Hello World Plugin

Slide 51

Slide 51 text

Plugin Build

Slide 52

Slide 52 text

compileKotlin { kotlinOptions { jvmTarget = "$JVM_TARGET_VERSION" freeCompilerArgs = [“-Xplugin=${project.rootDir}/libs/my-plugin-all.jar"] } } Add Plugin jar

Slide 53

Slide 53 text

fun main() { helloWorld() } fun helloWorld() { } HelloWorldKt src main kotlin com learn arrow-meta HelloWorldKt

Slide 54

Slide 54 text

fun main() { helloWorld() } fun helloWorld() { println("Hello DroidCon EMEA!”) } HelloWorld.kt Hello DroidCon EMEA! HelloWorldKt src main kotlin com learn arrow-meta HelloWorldKt

Slide 55

Slide 55 text

fun main() { helloWorld() } fun helloWorld() { } HelloWorldKt src main kotlin com learn arrow-meta HelloWorldKt

Slide 56

Slide 56 text

fun main() { helloWorld() } fun helloWorld() { } HelloWorldKt src main kotlin com learn arrow-meta HelloWorldKt

Slide 57

Slide 57 text

Writing Complier Plugins with Arrow Meta - Simple Hello World Plugin - Write & Test Plugin for logging - Combine Transformations - Custom Type Checker

Slide 58

Slide 58 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last()

Slide 59

Slide 59 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last() Annotation to log execution time

Slide 60

Slide 60 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last()

Slide 61

Slide 61 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long { println(""-> calculate(n=${'$'}n)") val startTime = System.currentTimeMillis() val result = listOf(1L,2L,3L).take(n).last() val timeToRun = System.currentTimeMillis() - startTime println(""<- calculate[ran in ${'$'}timeToRun ms]") return result }

Slide 62

Slide 62 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long { println(""-> calculate(n=${'$'}n)") val startTime = System.currentTimeMillis() val result = listOf(1L,2L,3L).take(n).last() val timeToRun = System.currentTimeMillis() - startTime println(""<- calculate[ran in ${'$'}timeToRun ms]") return result } Original Operation

Slide 63

Slide 63 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long { println(""-> calculate(n=${'$'}n)") val startTime = System.currentTimeMillis() val result = listOf(1L,2L,3L).take(n).last() val timeToRun = System.currentTimeMillis() - startTime println(""<- calculate[ran in ${'$'}timeToRun ms]") return result } Log method name & argument

Slide 64

Slide 64 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long { println(""-> calculate(n=${'$'}n)") val startTime = System.currentTimeMillis() val result = listOf(1L,2L,3L).take(n).last() val timeToRun = System.currentTimeMillis() - startTime println(""<- calculate[ran in ${'$'}timeToRun ms]") return result } Get current time

Slide 65

Slide 65 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long { println(""-> calculate(n=${'$'}n)") val startTime = System.currentTimeMillis() val result = listOf(1L,2L,3L).take(n).last() val timeToRun = System.currentTimeMillis() - startTime println(""<- calculate[ran in ${'$'}timeToRun ms]") return result } Original Operation

Slide 66

Slide 66 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long { println(""-> calculate(n=${'$'}n)") val startTime = System.currentTimeMillis() val result = listOf(1L,2L,3L).take(n).last() val timeToRun = System.currentTimeMillis() - startTime println(""<- calculate[ran in ${'$'}timeToRun ms]") return result } Calculate execution time

Slide 67

Slide 67 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long { println(""-> calculate(n=${'$'}n)") val startTime = System.currentTimeMillis() val result = listOf(1L,2L,3L).take(n).last() val timeToRun = System.currentTimeMillis() - startTime println(""<- calculate[ran in ${'$'}timeToRun ms]") return result } Log execution time

Slide 68

Slide 68 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long { println(""-> calculate(n=${'$'}n)") val startTime = System.currentTimeMillis() val result = listOf(1L,2L,3L).take(n).last() val timeToRun = System.currentTimeMillis() - startTime println(""<- calculate[ran in ${'$'}timeToRun ms]") return result } How do we generate this code?

Slide 69

Slide 69 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last()

Slide 70

Slide 70 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last() Filter

Slide 71

Slide 71 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin @DebugLog fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last() Modify

Slide 72

Slide 72 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Specify plugin name

Slide 73

Slide 73 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Specify Extension Phases

Slide 74

Slide 74 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Find Function

Slide 75

Slide 75 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Function

Slide 76

Slide 76 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Filter Function with DebugLog Annotation

Slide 77

Slide 77 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Create extension to find method

Slide 78

Slide 78 text

fun KtNamedFunction.validateFunction(): Boolean = Extension for getting a function with annotation

Slide 79

Slide 79 text

fun KtNamedFunction.validateFunction(): Boolean = hasOneIntParam() "&& hasLongReturnType() "&& hasAnnotation(DEBUG_LOG) Find method with a signature

Slide 80

Slide 80 text

fun KtNamedFunction.validateFunction(): Boolean = hasOneIntParam() "&& hasLongReturnType() "&& hasAnnotation(DEBUG_LOG) Check method has debug annotation

Slide 81

Slide 81 text

fun KtAnnotated.hasAnnotation( vararg annotationNames: String ): Boolean { val names = annotationNames.toHashSet() val predicate: (KtAnnotationEntry) "-> Boolean = { it.typeReference "?.typeElement "?.safeAs() "?.referencedName in names } return annotationEntries.any(predicate) } Annotations to match

Slide 82

Slide 82 text

fun KtAnnotated.hasAnnotation( vararg annotationNames: String ): Boolean { val names = annotationNames.toHashSet() val predicate: (KtAnnotationEntry) "-> Boolean = { it.typeReference "?.typeElement "?.safeAs() "?.referencedName in names } return annotationEntries.any(predicate) } Convert to set

Slide 83

Slide 83 text

fun KtAnnotated.hasAnnotation( vararg annotationNames: String ): Boolean { val names = annotationNames.toHashSet() val predicate: (KtAnnotationEntry) "-> Boolean = { it.typeReference "?.typeElement "?.safeAs() "?.referencedName in names } return annotationEntries.any(predicate) } Match annotation’s name

Slide 84

Slide 84 text

fun KtAnnotated.hasAnnotation( vararg annotationNames: String ): Boolean { val names = annotationNames.toHashSet() val predicate: (KtAnnotationEntry) "-> Boolean = { it.typeReference "?.typeElement "?.safeAs() "?.referencedName in names } return annotationEntries.any(predicate) } Check function’s annotations

Slide 85

Slide 85 text

fun KtAnnotated.hasAnnotation( vararg annotationNames: String ): Boolean { val names = annotationNames.toHashSet() val predicate: (KtAnnotationEntry) "-> Boolean = { it.typeReference "?.typeElement "?.safeAs() "?.referencedName in names } return annotationEntries.any(predicate) }

Slide 86

Slide 86 text

fun KtNamedFunction.validateFunction(): Boolean = hasOneIntParam() "&& hasLongReturnType() "&& hasAnnotation(DEBUG_LOG)

Slide 87

Slide 87 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Filter function

Slide 88

Slide 88 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Transform function

Slide 89

Slide 89 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Add debug log to function

Slide 90

Slide 90 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Modify Function

Slide 91

Slide 91 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" }

Slide 92

Slide 92 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" } Get info on function

Slide 93

Slide 93 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" } Create new function

Slide 94

Slide 94 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" }

Slide 95

Slide 95 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" }

Slide 96

Slide 96 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" }

Slide 97

Slide 97 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" }

Slide 98

Slide 98 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" }

Slide 99

Slide 99 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" }

Slide 100

Slide 100 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" }

Slide 101

Slide 101 text

fun replace(function: KtNamedFunction): String { val functionName = function.name val paramName = function.valueParameters.first().name val functionBody = function.body()"?.bodySourceAsExpression() return """ |fun ${functionName}(${paramName}: Int): Long { | println(""-> $functionName(${paramName}=$${paramName})") | val startTime = System.currentTimeMillis() | val result = $functionBody | val timeToRun = System.currentTimeMillis() - startTime | println(""<- ${functionName}[ran in ${'$'}timeToRun ms]") | return result | }""" }

Slide 102

Slide 102 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } Convert to function

Slide 103

Slide 103 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } val String.function get() = NamedFunction( delegate.createFunction(trimMargin().trim()) )

Slide 104

Slide 104 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) }

Slide 105

Slide 105 text

class DebugLogMetaPlugin : Meta { override fun intercept(ctx: CompilerContext): List = listOf( debugLog ) } Register Debug Log Plugin

Slide 106

Slide 106 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin fun main() { calculate(10) } @DebugLog fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last()

Slide 107

Slide 107 text

DebugLogPlugin src main kotlin com learn arrow-meta DebugLogPlugin fun main() { calculate(10) } @DebugLog fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last() DebugLogPlugin.kt "-> calculate(n=10) "<- calculate[ran in 36 ms]

Slide 108

Slide 108 text

val Meta.debugLog: CliPlugin get() = "DebugLog" { meta( namedFunction(this, { validateFunction() }) { c "-> Transform.replace( replacing = c, newDeclaration = replace(c).function ) } ) } How do we test this?

Slide 109

Slide 109 text

arrow-kt/arrow-meta Code ! Issues Pull Requests compiler-plugin 447 commits 2060af5 2 days ago ide-plugin gradle-plugin prelude docs meta-testing

Slide 110

Slide 110 text

arrow-kt/arrow-meta Code ! Issues Pull Requests compiler-plugin 447 commits 2060af5 2 days ago ide-plugin gradle-plugin prelude docs meta-testing Testing utilities

Slide 111

Slide 111 text

val source = """ |annotation class DebugLog | |@DebugLog |fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last() | """.trimMargin().trim().source

Slide 112

Slide 112 text

val source = """ |annotation class DebugLog | |@DebugLog |fun prime(n: Int): Long = listOf(1L,2L,3L).take(n).last() | """.trimMargin().trim().source val String.source = Code.Source(text = this)

Slide 113

Slide 113 text

val expectedOutput = """ |annotation class DebugLog | |fun calculate(n: Int): Long { | println(""-> calculate(n=${'$'}n)") | val startTime = System.currentTimeMillis() | val result = listOf(1L,2L,3L).take(n).last() | val timeToRun = System.currentTimeMillis() - startTime | println(""<- calculate[ran in ${'$'}timeToRun ms]") | return result | }""".trimMargin().trim().source Plugin should generate this

Slide 114

Slide 114 text

@Test fun `should log function time execution`() { }

Slide 115

Slide 115 text

@Test fun `should log function time execution`() { CompilerTest( config = { listOf(addMetaPlugins(DebugLogMetaPlugin())) }, code = { source }, assert = { quoteOutputMatches(expectedOutput) } ) } Testing utility

Slide 116

Slide 116 text

@Test fun `should log function time execution`() { CompilerTest( config = { listOf(addMetaPlugins(DebugLogMetaPlugin())) }, code = { source }, assert = { quoteOutputMatches(expectedOutput) } ) } Plugin to test

Slide 117

Slide 117 text

@Test fun `should log function time execution`() { CompilerTest( config = { listOf(addMetaPlugins(DebugLogMetaPlugin())) }, code = { source }, assert = { quoteOutputMatches(expectedOutput) } ) } Source to test

Slide 118

Slide 118 text

@Test fun `should log function time execution`() { CompilerTest( config = { listOf(addMetaPlugins(DebugLogMetaPlugin())) }, code = { source }, assert = { quoteOutputMatches(expectedOutput) } ) } Expected generate code

Slide 119

Slide 119 text

@Test fun `should log function time execution`() { CompilerTest( config = { listOf(addMetaPlugins(DebugLogMetaPlugin())) }, code = { debuglog }, assert = { quoteOutputMatches(expectedOutput) } ) } How do we run this test?

Slide 120

Slide 120 text

@Test fun `sshould log function time execution`() { CompilerTest( config = { listOf(addMetaPlugins(DebugLogMetaPlugin())) }, code = { debuglog }, assert = { quoteOutputMatches(expectedOutput) } ) } fun assertThis(compilerTest: CompilerTest) = compilerTest.run(interpreter)

Slide 121

Slide 121 text

@Test fun `should log function time execution`() { val compilerTest = CompilerTest(…) assertThis(compilerTest) } Run test

Slide 122

Slide 122 text

@Test fun `should log function time execution`() { assertThis(CompilerTest( config = { listOf(addMetaPlugins(DebugLogMetaPlugin())) }, code = { source}, assert = { quoteOutputMatches(expectedOutput) } )) } How does this test run?

Slide 123

Slide 123 text

val source = """ |annotation class DebugLog | |@DebugLog |fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last() | """.trimMargin().trim().source Source of compiler plugin

Slide 124

Slide 124 text

val source = """ |annotation class DebugLog | |@DebugLog |fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last() | """.trimMargin().trim().source DebugLogPlugin.kt START quote.doAnalysis: [KtFile: Source.kt] Create a file with source

Slide 125

Slide 125 text

val source = """ |annotation class DebugLog | |@DebugLog |fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last() | """.trimMargin().trim().source DebugLogPlugin.kt START quote.doAnalysis: [KtFile: Source.kt] Transform file into AST Transformed file: KtFile: Source.kt.

Slide 126

Slide 126 text

val source = """ |annotation class DebugLog | |@DebugLog |fun calculate(n: Int): Long = listOf(1L,2L,3L).take(n).last() | """.trimMargin().trim().source DebugLogPlugin.kt Transformed file: KtFile: Source.kt. [(KtFile: Source.kt, File(decls=[Structured(mods=[Lit(keyword=ANNOTATION)], ""... Structure of file

Slide 127

Slide 127 text

@Test fun `should log function time execution`() { assertThis(CompilerTest( config = { listOf(addMetaPlugins(DebugLogMetaPlugin())) }, code = { source}, assert = { quoteOutputMatches(expectedOutput) } )) } DebugLogPlugin.kt Transformed file: KtFile: Source.kt. [(KtFile: Source.kt, File(decls=[Structured(mods=[Lit(keyword=ANNOTATION)], ""... Run test

Slide 128

Slide 128 text

@Test fun `should log function time execution`() { assertThis(CompilerTest( config = { listOf(addMetaPlugins(DebugLogMetaPlugin())) }, code = { source}, assert = { quoteOutputMatches(expectedOutput) } )) } DebugLogPlugin.kt ktFile: KtFile: Source.kt ——————————————— fun calculate(n: Int): Long { println(""-> calculate(n=$n)") val startTime = System.curentTimeMillis() val result = listOf(1L,2L,3L).take(n).last() val timeToRun = System.currentTimeMillis() - startTime println(“"<- calculate[ran in $timeToRun ms]") return result } Generate log

Slide 129

Slide 129 text

Writing Complier Plugins with Arrow Meta - Simple Hello World Plugin - Write & Test Plugin for logging - Combine Transformations - Custom Type Checker

Slide 130

Slide 130 text

Transformations - Replace - Remove - New Sources - Combine Transformations

Slide 131

Slide 131 text

Quotes - Expressions - Declarations - Class & Object - Properties - Type Reference

Slide 132

Slide 132 text

fun main() { helloWorld() } fun helloWorld() { println("Hello DroidCon EMEA!”) } Remove ReplacePluginKt src main kotlin com learn arrow-meta ReplacePluginKt

Slide 133

Slide 133 text

val Meta.removePlugin: CliPlugin get() = “Remove" { meta( namedFunction(this, { name "== “helloWorld" }) { c "-> Transform.remove( removeIn = c, declaration = """println("Hello Droidcon EMEA!")""".expressionIn(c) ) } ) } Define Plugin

Slide 134

Slide 134 text

val Meta.removePlugin: CliPlugin get() = “Remove" { meta( namedFunction(this, { name "== “helloWorld" }) { c "-> Transform.remove( removeIn = c, declaration = """println("Hello Droidcon EMEA!")""".expressionIn(c) ) } ) } Quote

Slide 135

Slide 135 text

val Meta.removePlugin: CliPlugin get() = “Remove" { meta( namedFunction(this, { name "== “helloWorld" }) { c "-> Transform.remove( removeIn = c, declaration = """println("Hello Droidcon EMEA!")""".expressionIn(c) ) } ) } Filter function

Slide 136

Slide 136 text

val Meta.removePlugin: CliPlugin get() = “Remove" { meta( namedFunction(this, { name "== “helloWorld" }) { c "-> Transform.remove( removeIn = c, declaration = """println("Hello Droidcon EMEA!")""".expressionIn(c) ) } ) } NamedFunction

Slide 137

Slide 137 text

val Meta.removePlugin: CliPlugin get() = “Remove" { meta( namedFunction(this, { name "== “helloWorld" }) { c "-> Transform.remove( removeIn = c, declaration = """println("Hello Droidcon EMEA!")""".expressionIn(c) ) } ) }

Slide 138

Slide 138 text

val Meta.removePlugin: CliPlugin get() = “Remove" { meta( namedFunction(this, { name "== “helloWorld" }) { c "-> Transform.remove( removeIn = c, declaration = """println("Hello Droidcon EMEA!")""".expressionIn(c) ) } ) }

Slide 139

Slide 139 text

val Meta.removePlugin: CliPlugin get() = “Remove" { meta( namedFunction(this, { name "== “helloWorld" }) { c "-> Transform.remove( removeIn = c, declaration = """println("Hello Droidcon EMEA!")""".expressionIn(c) ) } ) } Template

Slide 140

Slide 140 text

val Meta.removePlugin: CliPlugin get() = “Remove" { meta( namedFunction(this, { name "== “helloWorld" }) { c "-> Transform.remove( removeIn = c, declaration = """println("Hello Droidcon EMEA!")""".expressionIn(c) ) } ) } Convert to expression

Slide 141

Slide 141 text

val Meta.removePlugin: CliPlugin get() = “Remove" { meta( namedFunction(this, { name "== “helloWorld" }) { c "-> Transform.remove( removeIn = c, declaration = """println("Hello Droidcon EMEA!")""".expressionIn(c) ) } ) }

Slide 142

Slide 142 text

fun main() { helloWorld() } fun helloWorld() { println("Hello DroidCon EMEA!”) } ReplacePluginKt ReplacePluginKt src main kotlin com learn arrow-meta ReplacePluginKt Greeting is not printed

Slide 143

Slide 143 text

Transformations - Replace - Remove - New Sources - Combine Transformations

Slide 144

Slide 144 text

NewSourcePluginKt src main kotlin com learn arrow-meta NewSourcePluginKt data class User(""...)

Slide 145

Slide 145 text

data class User(""...) NewSourcePluginKt src main kotlin com learn arrow-meta NewSourcePluginKt class UserAdapter_Generated { fun deserialize() = { } fun serialize() = { } } Generate adapter in new file

Slide 146

Slide 146 text

val Meta.adapterGenerator: CliPlugin get() = "Adapter Plugin" { meta( classDeclaration(this, { name "== "User" }) { Transform.newSources( """ package adapters class ${name}Adapter_Generated { fun deserialize() = { } fun serialize() = { } } """.file("${name}Adapter_Generated") ) } ) } Create Plugin

Slide 147

Slide 147 text

val Meta.adapterGenerator: CliPlugin get() = "Adapter Plugin" { meta( classDeclaration(this, { name "== "User" }) { Transform.newSources( """ package adapters class ${name}Adapter_Generated { fun deserialize() = { } fun serialize() = { } } """.file("${name}Adapter_Generated") ) } ) } Define Extension Phases

Slide 148

Slide 148 text

val Meta.adapterGenerator: CliPlugin get() = "Adapter Plugin" { meta( classDeclaration(this, { name "== "User" }) { Transform.newSources( """ package adapters class ${name}Adapter_Generated { fun deserialize() = { } fun serialize() = { } } """.file("${name}Adapter_Generated") ) } ) } Quote

Slide 149

Slide 149 text

val Meta.adapterGenerator: CliPlugin get() = "Adapter Plugin" { meta( classDeclaration(this, { name "== "User" }) { Transform.newSources( """ package adapters class ${name}Adapter_Generated { fun deserialize() = { } fun serialize() = { } } """.file("${name}Adapter_Generated") ) } ) } Find user class

Slide 150

Slide 150 text

val Meta.adapterGenerator: CliPlugin get() = "Adapter Plugin" { meta( classDeclaration(this, { name "== "User" }) { Transform.newSources( """ package adapters class ${name}Adapter_Generated { fun deserialize() = { } fun serialize() = { } } """.file("${name}Adapter_Generated") ) } ) } Transform class

Slide 151

Slide 151 text

val Meta.adapterGenerator: CliPlugin get() = "Adapter Plugin" { meta( classDeclaration(this, { name "== "User" }) { Transform.newSources( """ package adapters class ${name}Adapter_Generated { fun deserialize() = { } fun serialize() = { } } """.file("${name}Adapter_Generated") ) } ) } Create adapter in new file

Slide 152

Slide 152 text

val Meta.adapterGenerator: CliPlugin get() = "Adapter Plugin" { meta( classDeclaration(this, { name "== "User" }) { Transform.newSources( """ package adapters class ${name}Adapter_Generated { fun deserialize() = { } fun serialize() = { } } """.file("${name}Adapter_Generated") ) } ) } Adapter

Slide 153

Slide 153 text

val Meta.adapterGenerator: CliPlugin get() = "Adapter Plugin" { meta( classDeclaration(this, { name "== "User" }) { Transform.newSources( """ package adapters class ${name}Adapter_Generated { fun deserialize() = { } fun serialize() = { } } """.file("${name}Adapter_Generated") ) } ) } Create a new file

Slide 154

Slide 154 text

data class User(""...) NewSourcePluginKt src main kotlin com learn arrow-meta NewSourcePluginKt

Slide 155

Slide 155 text

NewSourcePluginKt src main kotlin com learn arrow-meta UserAdapter_Generated.kt class UserAdapter_Generated { fun deserialize() = { } fun serialize() = { } } Generated file

Slide 156

Slide 156 text

Transformations - Replace - Remove - New Sources - Combine Transformations

Slide 157

Slide 157 text

CombineTransformKt src main kotlin com learn arrow-meta CombineTransformKt public class SimpleClass { fun printGreeting() { println("Hello DroidCon EMEA!”) } }

Slide 158

Slide 158 text

CombineTransformKt src main kotlin com learn arrow-meta CombineTransformKt public class SimpleClass { fun printGreeting() { println("Hello DroidCon EMEA!”) } } Change class visibility to private

Slide 159

Slide 159 text

CombineTransformKt src main kotlin com learn arrow-meta CombineTransformKt public class SimpleClass { fun printGreeting() { println("Hello DroidCon EMEA!”) } } Remove Expression

Slide 160

Slide 160 text

CombineTransformKt src main kotlin com learn arrow-meta CombineTransformKt private class SimpleClass { ""... }

Slide 161

Slide 161 text

val Meta.composeTransforms: CliPlugin get() = "Compose Transforms" { meta( classDeclaration(this, { name "== "ManySimpleCase" }) { c "-> changeClassVisibility("ManySimpleCase", c, this) + removeGreeting(c, this) } ) }

Slide 162

Slide 162 text

val Meta.composeTransforms: CliPlugin get() = "Compose Transforms" { meta( classDeclaration(this, { name "== "SimpleClass" }) { c "-> changeClassVisibility("SimpleClass", c, this) + removeGreeting(c, this) } ) } Filter Class

Slide 163

Slide 163 text

val Meta.composeTransforms: CliPlugin get() = "Compose Transforms" { meta( classDeclaration(this, { name "== "SimpleClass" }) { c "-> changeClassVisibility(“SimpleClass", c, this) + removeGreeting(c, this) } ) } Change Class visible to private

Slide 164

Slide 164 text

fun CompilerContext.changeClassVisibility( className: String, context: KtClass, declaration: ClassDeclaration ): Transform = declaration.run { } SimpleClass

Slide 165

Slide 165 text

fun CompilerContext.changeClassVisibility( className: String, context: KtClass, declaration: ClassDeclaration ): Transform = declaration.run { } Class to transform

Slide 166

Slide 166 text

fun CompilerContext.changeClassVisibility( className: String, context: KtClass, declaration: ClassDeclaration ): Transform = declaration.run { } Class’s Declaration

Slide 167

Slide 167 text

fun CompilerContext.changeClassVisibility(""...): Transform = declaration.run { Transform.replace( replacing = context, newDeclaration = """ | private class $className { | $body | } """.`class`.syntheticScope ) }

Slide 168

Slide 168 text

fun CompilerContext.changeClassVisibility(""...): Transform = declaration.run { Transform.replace( replacing = context, newDeclaration = """ | private class $className { | $body | } """.`class`.syntheticScope ) } New Identifier

Slide 169

Slide 169 text

val Meta.composeTransforms: CliPlugin get() = "Compose Transforms" { meta( classDeclaration(this, { name "== "SimpleClass" }) { c "-> changeClassVisibility(“SimpleClass", c, this) + removeGreeting(c, this) } ) } Remove Greeting

Slide 170

Slide 170 text

fun CompilerContext.removeGreeting( context: KtClass, declaration: ClassDeclaration ): Transform = declaration.run { Transform.remove( removeIn = context, declaration = """ fun printFirst() = println("Hello DroidCon EMEA!") """.expressionIn(context) ) }

Slide 171

Slide 171 text

fun CompilerContext.removeGreeting( context: KtClass, declaration: ClassDeclaration ): Transform = declaration.run { Transform.remove( removeIn = context, declaration = """ fun printGreeting() = println("Hello DroidCon EMEA!") """.expressionIn(context) ) } Remove Method

Slide 172

Slide 172 text

val Meta.composeTransforms: CliPlugin get() = "Compose Transforms" { meta( classDeclaration(this, { name "== "SimpleClass" }) { c "-> changeClassVisibility(“SimpleClass", c, this) + removeGreeting(c, this) } ) } Combine?

Slide 173

Slide 173 text

val Meta.composeTransforms: CliPlugin get() = "Compose Transforms" { meta( classDeclaration(this, { name "== "SimpleClass" }) { c "-> changeClassVisibility(“SimpleClass", c, this) + removeGreeting(c, this) } ) } Use Plus Operator + +

Slide 174

Slide 174 text

operator fun Transform.plus(transform: Transform): Transform.Many(arrayListOf(this, transform)) Transform 1 Transform 2

Slide 175

Slide 175 text

operator fun Transform.plus(transform: Transform): Transform.Many(arrayListOf(this, transform)) New Combine Transform

Slide 176

Slide 176 text

val Meta.composeTransforms: CliPlugin get() = "Compose Transforms" { meta( classDeclaration(this, { name "== "SimpleClass" }) { c "-> changeClassVisibility(“SimpleClass", c, this) + removeGreeting(c, this) } ) } Remove Greeting

Slide 177

Slide 177 text

CombineTransformKt src main kotlin com learn arrow-meta CombineTransformKt private class SimpleClass { ""... }

Slide 178

Slide 178 text

Transformations - Replace - Remove - New Sources - Combine Transformations

Slide 179

Slide 179 text

Writing Complier Plugins with Arrow Meta - Simple Hello World Plugin - Write & Test Plugin for logging - Combine Transformations - Custom Type Checker

Slide 180

Slide 180 text

Type 1 Type 2

Slide 181

Slide 181 text

Type 1 Type 2 "== Define equality & Subtyping

Slide 182

Slide 182 text

val Meta.typeChecker: CliPlugin get() = “Custom Type Checker” { meta( typeChecker { ProofTypeChecker(ctx) } }

Slide 183

Slide 183 text

val Meta.typeChecker: CliPlugin get() = “Custom Type Checker” { meta( typeChecker { CustomTypeChecker(ctx) } }

Slide 184

Slide 184 text

class CustomTypeChecker() : NewKotlinTypeChecker { override fun isSubtypeOf( subtype: KotlinType, supertype: KotlinType ): Boolean { override fun equalTypes( subtype: KotlinType, supertype: KotlinType ): Boolean = } Type Checker Provided by Kotlin Compiler

Slide 185

Slide 185 text

class CustomTypeChecker() : NewKotlinTypeChecker { override fun isSubtypeOf( subtype: KotlinType, supertype: KotlinType ): Boolean override fun equalTypes( subtype: KotlinType, supertype: KotlinType ): Boolean } Define Sub-typing

Slide 186

Slide 186 text

class CustomTypeChecker() : NewKotlinTypeChecker { override fun isSubtypeOf( subtype: KotlinType, supertype: KotlinType ): Boolean override fun equalTypes( subtype: KotlinType, supertype: KotlinType ): Boolean } Define Equality

Slide 187

Slide 187 text

class CustomTypeChecker() : NewKotlinTypeChecker { override fun isSubtypeOf( subtype: KotlinType, supertype: KotlinType ): Boolean override fun equalTypes( subtype: KotlinType, supertype: KotlinType ): Boolean } Define Equality

Slide 188

Slide 188 text

Custom Type Checking Example - Type Proofs Plugin - Refinement Type Plugin

Slide 189

Slide 189 text

IDE Plugins

Slide 190

Slide 190 text

class TasksViewModel { fun refreshTasks() { setState { } } }

Slide 191

Slide 191 text

class TasksViewModel { fun refreshTasks() { setState { ""... } } } Show icon in IDE

Slide 192

Slide 192 text

val MetaIde.mviPlugin: IdePlugin Create extension property

Slide 193

Slide 193 text

val MetaIde.mviPlugin: IdePlugin Create an IDE Plugin

Slide 194

Slide 194 text

val MetaIde.mviPlugin: IdePlugin typealias IdePlugin = Plugin

Slide 195

Slide 195 text

val MetaIde.mviPlugin: IdePlugin get() = “MVI Plugin” { } Create Plugin object

Slide 196

Slide 196 text

val MetaIde.mviPlugin: IdePlugin get() = “MVI Plugin” { } operator fun String.invoke( phases: IdeContext.() "-> List ): IdePlugin

Slide 197

Slide 197 text

val MetaIde.mviPlugin: IdePlugin get() = “MVI Plugin" { meta( ) } Add extension phases

Slide 198

Slide 198 text

val MetaIde.mviPlugin: IdePlugin get() = “MVI Plugin".invoke { meta( addLineMarkerProvider( icon = PluginIcons.UPDATE, composite = KtNamedFunction"::class.java, message = { f: KtNamedFunction "-> “State will be updated by action.” }, transform = { it.safeAs()"?.takeIf { f "-> f.name "== "setState" } } ) ) } Add marker for a node in AST

Slide 199

Slide 199 text

val MetaIde.mviPlugin: IdePlugin get() = “MVI Plugin".invoke { meta( addLineMarkerProvider( icon = PluginIcons.UPDATE, composite = KtNamedFunction"::class.java, message = { f: KtNamedFunction "-> “State will be updated by action.” }, transform = { it.safeAs()"?.takeIf { f "-> f.name "== "setState" } } ) ) } Path to icon in resources

Slide 200

Slide 200 text

val MetaIde.mviPlugin: IdePlugin get() = “MVI Plugin".invoke { meta( addLineMarkerProvider( icon = PluginIcons.UPDATE, composite = KtNamedFunction"::class.java, message = { f: KtNamedFunction "-> “State will be updated by action.” }, transform = { it.safeAs()"?.takeIf { f "-> f.name "== "setState" } } ) ) } AST Node

Slide 201

Slide 201 text

val MetaIde.mviPlugin: IdePlugin get() = “MVI Plugin".invoke { meta( addLineMarkerProvider( icon = PluginIcons.UPDATE, composite = KtNamedFunction"::class.java, message = { f: KtNamedFunction "-> “State will be updated by action.” }, transform = { it.safeAs()"?.takeIf { f "-> f.name "== "setState" } } ) ) } Message to show on mouse over

Slide 202

Slide 202 text

val MetaIde.mviPlugin: IdePlugin get() = “MVI Plugin".invoke { meta( addLineMarkerProvider( icon = PluginIcons.UPDATE, composite = KtNamedFunction"::class.java, message = { f: KtNamedFunction "-> “State will be updated by action.” }, transform = { it.safeAs()"?.takeIf { f "-> f.name "== "setState" } } ) ) } Filter function with property

Slide 203

Slide 203 text

val MetaIde.mviPlugin: IdePlugin get() = “MVI Plugin".invoke { meta( addLineMarkerProvider( icon = PluginIcons.UPDATE, composite = KtNamedFunction"::class.java, message = { f: KtNamedFunction "-> “State will be updated by action.” }, transform = { it.safeAs()"?.takeIf { f "-> f.name "== "setState" } } ) ) }

Slide 204

Slide 204 text

class IdeMetaPlugin : MetaPlugin(),{ override fun intercept(ctx: IdeContext): List = listOf( mviPlugin ) } Extend MetaPlugin

Slide 205

Slide 205 text

class IdeMetaPlugin : MetaPlugin() { override fun intercept(ctx: IdeContext): List = listOf( mviPlugin ) } Add all IDE plugins

Slide 206

Slide 206 text

class TasksViewModel { fun refreshTasks() { setState { ""... } } } Show icon in IDE

Slide 207

Slide 207 text

class TasksViewModel { fun refreshTasks() { setState { ""... } } } Show message for feature State will be updated by action.

Slide 208

Slide 208 text

Plugins in Arrow Meta ● Monad Comprehensions ● Lens ● Type Proofs

Slide 209

Slide 209 text

https:"//codingwithmohit.com/arrow-meta/writing-compiler-plugin-with-arrow-meta/ Compiler Plugin

Slide 210

Slide 210 text

Thank you Arrow Team & All Contributors ● Rachel M. Carmena ● Raúl Raja ● Imran Settuba ● Simon Veragauwen ● and all contributors!

Slide 211

Slide 211 text

Kotlin Slack Channels ● #compiler ● #arrow-meta

Slide 212

Slide 212 text

Resources ● Writing Kotlin Compiler Plugins with Arrow Meta ● Lambda World 2019 - Arrow Meta - Enabling Functional Programming in the Kotlin Compiler ● Arrow Meta. Kotlin Metaprogramming for the masses ● Type Proofs and FP for the Kotlin Type System

Slide 213

Slide 213 text

Thank You! www.twitter.com/heyitsmohit www.codingwithmohit.com