Skip to content

Instantly share code, notes, and snippets.

@nomisRev
Last active August 28, 2023 06:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nomisRev/e01ddc354c84b8b626c23d024706b916 to your computer and use it in GitHub Desktop.
Save nomisRev/e01ddc354c84b8b626c23d024706b916 to your computer and use it in GitHub Desktop.
Migration script for Arrow 1.2.0 to migrate towards arrow.core.raise.*

Arrow 1.2.0 Raise migration script

This migration script attempts to automatically migrate arrow.core.computations.* and arrow.core.continuations.* on a best effort to arrow.core.raise.*. It has been tested on serveral real-life projects with 100% success, being able to automatically migrate the entire codebase.

The run this kts script you need kotlinc install on your machine. The official documentation on how to install kotlinc.

Some methods like ensure in the DSL became top-level, and fold if you're using Effect or EagerEffect. These new top-level imports cannot be automatically migrated, and there are two ways of dealing with the necessary imports.

There is two ways to use this script for migration:

  • Recommended: automatic imports handling, adds too many imports and uses IntelliJ's optimise imports
  • Manual imports, doesn't add import for fold, and ensure and requires manually importing them on a usage basis.

Recommended usage

Once installed you can run the script with default params: kotlinc -script migrate.main.kts ..

You need to have Arrow version 1.2.0 (1.1.6-alpha.28) in order to compile your project after the script finishes running.

The script might leave you with some unused imports, to fix this you can run optimise imports on your project root or src folder.

  • Select src or _project root+⌃ ⌥ OorCtrl+Alt+O`.
  • Right-click project root or src in project view, and select Optimise imports

This should remove all unused imports this might also affect other unrelated imports.

Alternative

If you don't want to rely on IntelliJ's optimise imports you can still use the migration script to do 99,99% of the work, except import ensure (and fold for Effect/EagerEffect).

Easiest way to fix the imports is run ./gradlew build and add missing imports in files that fail to compile.

Thank you for using Arrow, and your support. I hope this script was able to simplify your migration process to 2.0.0

@file:DependsOn("io.arrow-kt:arrow-fx-coroutines-jvm:1.1.3")
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4")
import arrow.fx.coroutines.parMapUnordered
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.runBlocking
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.io.path.Path
import kotlin.io.path.isDirectory
fun String.replace(atomic: AtomicBoolean): List<String> {
fun List<String>.addExtraImports(): List<String> =
if (atomic.getAndSet(false)) this + listOf("import arrow.core.raise.fold", "import arrow.core.raise.ensure")
else this
return if (startsWith("import")) when (this) {
"import arrow.core.continuations.either" -> listOf("import arrow.core.raise.either").addExtraImports()
"import arrow.core.computations.either" -> listOf("import arrow.core.raise.either").addExtraImports()
"import arrow.core.continuations.either.eager" -> listOf("import arrow.core.raise.either").addExtraImports()
"import arrow.core.computations.option" -> listOf("import arrow.core.raise.option").addExtraImports()
"import arrow.core.continuations.option" -> listOf("import arrow.core.raise.option").addExtraImports()
"import arrow.core.continuations.option.eager" -> listOf("import arrow.core.raise.option").addExtraImports()
"import arrow.core.computations.nullable" -> listOf("import arrow.core.raise.nullable").addExtraImports()
"import arrow.core.continuations.nullable" -> listOf("import arrow.core.raise.nullable").addExtraImports()
"import arrow.core.continuations.nullable.eager" -> listOf("import arrow.core.raise.nullable").addExtraImports()
"import arrow.core.computations.result" -> listOf("import arrow.core.raise.result").addExtraImports()
"import arrow.core.continuations.result" -> listOf("import arrow.core.raise.result").addExtraImports()
"import arrow.core.continuations.result.eager" -> listOf("import arrow.core.raise.result").addExtraImports()
"import arrow.core.computations.ior" -> listOf("import arrow.core.raise.ior").addExtraImports()
"import arrow.core.continuations.ior" -> listOf("import arrow.core.raise.ior").addExtraImports()
"import arrow.core.continuations.ior.eager" -> listOf("import arrow.core.raise.ior").addExtraImports()
"import arrow.core.computations.ensureNotNull" -> listOf("import arrow.core.raise.ensureNotNull")
"import arrow.core.continuations.ensureNotNull" -> listOf("import arrow.core.raise.ensureNotNull")
"import arrow.core.continuations.effect" -> listOf("import arrow.core.raise.effect").addExtraImports()
"import arrow.core.continuations.eagerEffect" -> listOf("import arrow.core.raise.eagerEffect").addExtraImports()
"import arrow.core.continuations.EagerEffectScope" -> listOf("import arrow.core.raise.Raise").addExtraImports()
"import arrow.core.continuations.EffectScope" -> listOf("import arrow.core.raise.Raise").addExtraImports()
else -> listOf(this)
} else {
replace("either.eager {", "either {")
.replace("either.eager<", "either<")
.replace("option.eager {", "option {")
.replace("option.eager<", "option<")
.replace("nullable.eager {", "nullable {")
.replace("nullable.eager<", "nullable<")
.replace("result.eager {", "result {")
.replace("result.eager<", "result<")
.replace("ior.eager {", "ior {")
.replace("ior.eager<", "ior<")
.replace("EagerEffectScope<", "Raise<")
.replace("EffectScope<", "Raise<")
.let { listOf(it) }
}
}
fun File.process(addImports: Boolean): Unit = useLines { lines ->
val atomic = AtomicBoolean(addImports)
Files.write(Paths.get(absolutePath), lines.flatMap { it.replace(atomic) }.toList())
}
if (args.isEmpty()) {
throw RuntimeException("You need to point to a directory that contains your .kt files")
} else {
val path = Path(args[0])
val addImports = args.getOrNull(1)?.toBoolean() ?: true
val maxConcurrency = args.getOrNull(2)?.toIntOrNull() ?: 8
if (path.isDirectory()) runBlocking {
path.toFile().walk().asFlow()
.filter { it.extension == "kt" }
.parMapUnordered(maxConcurrency) { file ->
println("Processing $file")
file.process(addImports)
}.flowOn(Dispatchers.IO).collect()
} else path.toFile()
.also { println("Processing $it") }
.process(addImports)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment