https://kotlinlang.org logo
Join the conversationJoin Slack
Channels
100daysofcode
100daysofkotlin
100daysofkotlin-2021
advent-of-code
aem
ai
alexa
algeria
algolialibraries
amsterdam
android
android-architecture
android-databinding
android-studio
androidgithubprojects
androidthings
androidx
androidx-xprocessing
anime
anko
announcements
apollo-kotlin
appintro
arabic
argentina
arkenv
arksemdevteam
armenia
arrow
arrow-contributors
arrow-meta
ass
atlanta
atm17
atrium
austin
australia
austria
awesome-kotlin
ballast
bangladesh
barcelona
bayarea
bazel
beepiz-libraries
belgium
berlin
big-data
books
boston
brazil
brikk
budapest
build
build-tools
bulgaria
bydgoszcz
cambodia
canada
carrat
carrat-dev
carrat-feed
chicago
chile
china
chucker
cincinnati-user-group
cli
clikt
cloudfoundry
cn
cobalt
code-coverage
codeforces
codemash-precompiler
codereview
codingame
codingconventions
coimbatore
collaborations
colombia
colorado
communities
competitive-programming
competitivecoding
compiler
compose
compose-android
compose-desktop
compose-hiring
compose-ios
compose-mp
compose-ui-showcase
compose-wear
compose-web
connect-audit-events
corda
cork
coroutines
couchbase
coursera
croatia
cryptography
cscenter-course-2016
cucumber-bdd
cyprus
czech
dagger
data2viz
databinding
datascience
dckotlin
debugging
decompose
decouple
denmark
deprecated
detekt
detekt-hint
dev-core
dfw
docs-revamped
dokka
domain-driven-design
doodle
dsl
dublin
dutch
eap
eclipse
ecuador
edinburgh
education
effective-kotlin
effectivekotlin
emacs
embedded-kotlin
estatik
event21-community-content
events
exposed
failgood
fb-internal-demo
feed
firebase
flow
fluid-libraries
forkhandles
forum
fosdem
fp-in-kotlin
framework-elide
freenode
french
fritz2
fuchsia
functional
funktionale
gamedev
ge-kotlin
general-advice
georgia
geospatial
german-lang
getting-started
github-workflows-kt
glance
godot-kotlin
google-io
gradle
graphic
graphkool
graphql
graphql-kotlin
graviton-browser
greece
grpc
gsoc
gui
hackathons
hacktoberfest
hamburg
hamkrest
helios
helsinki
hexagon
hibernate
hikari-cp
hire-me
hiring
hongkong
hoplite
http4k
hungary
hyderabad
image-processing
india
indonesia
inkremental
intellij
intellij-plugins
intellij-tricks
internships
introduce-yourself
io
ios
iran
israel
istanbulcoders
italian
jackson-kotlin
jadx
japanese
jasync-sql
java-to-kotlin-refactoring
javadevelopers
javafx
javalin
javascript
jdbi
jhipster-kotlin
jobsworldwide
jpa
jshdq
juul-libraries
jvm-ir-backend-feedback
jxadapter
k2-early-adopters
kaal
kafka
kakao
kalasim
kapt
karachi
karg
karlsruhe
kash_shell
kaskade
kbuild
kdbc
kgen-doc-tools
kgraphql
kinta
klaxon
klock
kloudformation
kmdc
kmm-español
kmongo
knbt
knote
koalaql
koans
kobalt
kobweb
kodein
kodex
kohesive
koin
koin-dev
komapper
kondor-json
kong
kontent
kontributors
korau
korean
korge
korim
korio
korlibs
korte
kotest
kotest-contributors
kotless
kotlick
kotlin-asia
kotlin-beam
kotlin-by-example
kotlin-csv
kotlin-data-storage
kotlin-foundation
kotlin-fuel
kotlin-in-action
kotlin-inject
kotlin-latam
kotlin-logging
kotlin-multiplatform-contest
kotlin-mumbai
kotlin-native
kotlin-pakistan
kotlin-plugin
kotlin-pune
kotlin-roadmap
kotlin-samples
kotlin-sap
kotlin-serbia
kotlin-spark
kotlin-szeged
kotlin-website
kotlinacademy
kotlinbot
kotlinconf
kotlindl
kotlinforbeginners
kotlingforbeginners
kotlinlondon
kotlinmad
kotlinprogrammers
kotlinsu
kotlintest
kotlintest-devs
kotlintlv
kotlinultimatechallenge
kotlinx-datetime
kotlinx-files
kotlinx-html
kotrix
kotson
kovenant
kprompt
kraph
krawler
kroto-plus
ksp
ktcc
ktfmt
ktlint
ktor
ktp
kubed
kug-leads
kug-torino
kvision
kweb
lambdaworld_cadiz
lanark
language-evolution
language-proposals
latvia
leakcanary
leedskotlinusergroup
lets-have-fun
libgdx
libkgd
library-development
linkeddata
lithuania
london
losangeles
lottie
love
lychee
macedonia
machinelearningbawas
madrid
malaysia
mathematics
meetkotlin
memes
meta
metro-detroit
mexico
miami
micronaut
minnesota
minutest
mirror
mockk
moko
moldova
monsterpuzzle
montreal
moonbean
morocco
motionlayout
mpapt
mu
multiplatform
mumbai
munich
mvikotlin
mvrx
myndocs-oauth2-server
naming
navigation-architecture-component
nepal
new-mexico
new-zealand
newname
nigeria
nodejs
norway
npm-publish
nyc
oceania
ohio-kotlin-users
oldenburg
oolong
opensource
orbit-mvi
osgi
otpisani
package-search
pakistan
panamá
pattern-matching
pbandk
pdx
peru
philippines
phoenix
pinoy
pocketgitclient
polish
popkorn
portugal
practical-functional-programming
proguard
prozis-android-backup
pyhsikal
python
python-contributors
quasar
random
re
react
reaktive
realm
realworldkotlin
reductor
reduks
redux
redux-kotlin
refactoring-to-kotlin
reflect
refreshversions
reports
result
rethink
revolver
rhein-main
rocksdb
romania
room
rpi-pico
rsocket
russian
russian_feed
russian-kotlinasfirst
rx
rxjava
san-diego
science
scotland
scrcast
scrimage
script
scripting
seattle
serialization
server
sg-user-group
singapore
skia-wasm-interop-temp
skrape-it
slovak
snake
sofl-user-group
southafrica
spacemacs
spain
spanish
speaking
spek
spin
splitties
spotify-mobius
spring
spring-security
squarelibraries
stackoverflow
stacks
stayhungrystayfoolish
stdlib
stlouis
strife-discord-lib
strikt
students
stuttgart
sudan
swagger-gradle-codegen
swarm
sweden
swing
swiss-user-group
switzerland
talking-kotlin
tallinn
tampa
teamcity
tegal
tempe
tensorflow
terminal
test
testing
testtestest
texas
tgbotapi
thailand
tornadofx
touchlab-tools
training
tricity-kotlin-user-group
trójmiasto
truth
tunisia
turkey
turkiye
twitter-feed
uae
udacityindia
uk
ukrainian
uniflow
unkonf
uruguay
utah
uuid
vancouver
vankotlin
vertx
videos
vienna
vietnam
vim
vkug
vuejs
web-mpp
webassembly
webrtc
wimix_sentry
wwdc
zircon
Powered by Linen
compiler
  • m

    mp

    10/25/2019, 6:41 PM
    Is there a way to configure the compiler to emit bytecode that has
    @Nullable
    for a method that returns a nullable type, etc, so that in plain Java code the IDE can provide the same suggestions that it would for a
    @Nullable
    Java method?
    i
    • 2
    • 5
  • j

    jossiwolf

    11/03/2019, 7:49 PM
    Are there any resources on how
    ExpressionCodegen
    works? Like anything, I'm just trying to get started with a modification on it and trying to understand a bit more
    i
    • 2
    • 1
  • w

    wiyarmir

    11/04/2019, 4:24 PM
    has anyone successfully overridden
    toString()
    in a data class or is the limitation stated here still a problem? https://github.com/ZacSweers/redacted-compiler-plugin
    r
    j
    +2
    • 5
    • 35
  • j

    jereksel

    11/04/2019, 10:45 PM
    Hi, method getSyntheticFunctionNames in extension SyntheticResolveExtension is called for every class in module (as expected), but it's not called for classes from dependencies. Is there a way to add methods to autocomplete for already compiled classes?
    r
    • 2
    • 7
  • j

    jdemeulenaere

    11/08/2019, 9:37 PM
    Are there compiler APIs I can use to incrementally analyze files ? This is how I am analyzing Kotlin code atm, but except keeping the KotlinCoreEnvironment between analysis I don't do any caching/incremental analysis. Any idea how to achieve that ?
    import com.intellij.openapi.Disposable
    import com.intellij.psi.PsiFileFactory
    import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
    import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace
    import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
    import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
    import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
    import org.jetbrains.kotlin.config.CommonConfigurationKeys
    import org.jetbrains.kotlin.config.CompilerConfiguration
    import org.jetbrains.kotlin.config.JVMConfigurationKeys
    import org.jetbrains.kotlin.config.JvmTarget
    import org.jetbrains.kotlin.container.get
    import org.jetbrains.kotlin.idea.KotlinLanguage
    import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
    import org.jetbrains.kotlin.psi.KtFile
    import org.jetbrains.kotlin.resolve.BindingContext
    import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
    import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
    import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
    import kotlin.reflect.jvm.internal.impl.storage.StorageManager
    
    class Analyzer {
      private val compilerConfiguration = CompilerConfiguration().apply {
        put(CommonConfigurationKeys.MODULE_NAME, JvmProtoBufUtil.DEFAULT_MODULE_NAME)
        put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, StdOutMessageCollector)
        put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
      }
    
      private val kotlinEnvironment = KotlinCoreEnvironment.createForProduction(
        parentDisposable = Disposable { },
        configuration = compilerConfiguration,
        configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES
      )
    
      private val project = kotlinEnvironment.project
      private val psiFactory = PsiFileFactory.getInstance(project)
    
      fun analyzeCode(code: String): Pair<KtFile, BindingContext> {
        val file = createKotlinFile(code)
        return file to analyze(file)
      }
    
      private fun createKotlinFile(code: String): KtFile {
        val file = psiFactory.createFileFromText("Dumb.kt", KotlinLanguage.INSTANCE, code, true, false)
        return file as KtFile
      }
    
      private fun analyze(psiFile: KtFile): BindingContext {
        val files = arrayListOf(psiFile)
    
        val trace = CliBindingTrace()
        val container = TopDownAnalyzerFacadeForJVM.createContainer(
          project,
          emptyList(),
          trace,
          kotlinEnvironment.configuration,
          kotlinEnvironment::createPackagePartProvider,
          { storageManager, _ -> FileBasedDeclarationProviderFactory(storageManager, files) }
        )
    
        val analyzer = container.get<LazyTopDownAnalyzer>()
        analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
        return trace.bindingContext
      }
    }
    t
    • 2
    • 1
  • j

    jereksel

    11/11/2019, 1:38 PM
    Or maybe for CLI I should treat imports not as "generate all for autocomplete", but as "check if this import is valid".
    • 1
    • 1
  • r

    raulraja

    11/12/2019, 11:03 PM
    Hi, I’m trying to find the service or code in the compiler sources responsible to check a given
    FunctionDescriptor
    that points to something like:
    fun <A> A.show(): String = toString()
    is a valid candidate to be applied in the expression
    val s: String = 1.show()
    When I have
    A
    and
    Int
    as `KotlinType`s and I ask if
    Int
    is a subtype of
    A
    it always says no despite being a generic type. This makes sense since it could refer to any other type. What service does the Kotlin compiler use to see if a generic function is in bounds or applicable to a type? Any help is appreciated, thanks.
    d
    i
    • 3
    • 10
  • f

    Fudge

    11/18/2019, 7:56 PM
    I've been trying to create a tool to perform class/methods/field name migrations for Kotlin files. I have tried using the
    kotlin-compiler
    package to parse the Kotlin files, perform PSI renaming transformations, and then write the transformed PSI as text. However, when I try to use the PSI
    add
    and
    replace
    methods, I get the following error:
    java.lang.IllegalArgumentException: Missing extension point: com.intellij.treeCopyHandler in area null
    Is this the right way of going about this? If it is, how do I resolve this error?
    r
    • 2
    • 5
  • t

    turansky

    11/23/2019, 10:12 AM
    Kotlin compiler plugin generate synthetic
    companion object
    = I need assotiated IDEA plugin for comfort work with this
    companion object
    ?
    r
    h
    • 3
    • 31
  • s

    spierce7

    11/25/2019, 4:34 AM
    I had found some information a few months ago on the Kotlin compiler plugin api, but I can’t seem to find that now. Any chance anyone could point me towards some information?
    s
    t
    • 3
    • 5
  • j

    josephivie

    11/26/2019, 4:34 AM
    While using the incremental JVM compiler, this line is removing my
    kotlin_module
    file before re-running the compiler on changed files, which leads to compilation failure due to having no access to package-owned declarations (ie extension functions, typealiases, top level functions). Is this a bug, or am I using the incremental compiler wrong? https://github.com/JetBrains/kotlin/blob/master/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalCompilerRunner.kt#L219
    • 1
    • 2
  • f

    Fatih Yalmanbas

    11/29/2019, 11:23 AM
    Would it be possible to default classes and functions visibility modifiers to
    internal
    ? Extra points if i do not need to use an annotation to tag those classes.
    w
    t
    • 3
    • 6
  • j

    jdemeulenaere

    12/09/2019, 12:50 PM
    Will all the descriptor classes disappear with the new compiler ? E.g.
    DeclarationDescriptor
    ,
    FunctionDescriptor
    , etc . Same question for
    BindingContext
    :yes: 1
    i
    d
    • 3
    • 7
  • j

    jdemeulenaere

    12/10/2019, 10:39 AM
    Is there a better (meaning faster) way than the code below to get all top level extension functions on a certain
    KotlinType
    ? It works but it is pretty slow as it fetches all top level extensions that are available (even the ones not imported). I can add restriction on the package and only get imported functions if it can help make things faster. Thanks a lot 🙂
    fun getExtensionsOnType(type: KotlinType, scope: LexicalScope): List<DeclarationDescriptor> {
      val extensionKindFilter = kindFilter exclude DescriptorKindExclude.NonExtensions
      return scope.collectDescriptorsFiltered(extensionKindFilter, nameFilter)
        .filter {
          val receiverType = (it as? CallableDescriptor)?.extensionReceiverParameter?.value?.type
          receiverType != null && this.isSubtypeOf(receiverType)
        }
    }
    r
    • 2
    • 2
  • s

    Sujit

    12/18/2019, 4:36 AM
    Is it possible to tell kotlin compiler to avoid generating deprecated methods like this:
    @kotlin.Deprecated public constructor
    in the
    class
    file?
    r
    • 2
    • 2
  • r

    raulraja

    12/18/2019, 5:09 PM
    Related to the compiler and the work we are doing in Arrow Meta

    https://www.youtube.com/watch?v=n9smQNxUyBI▾

    @amanda.hinchman-dominguez and @Rachel talk goes over compiler phases and meta programming with the compiler.
    e
    • 2
    • 1
  • l

    Leland Takamine

    12/19/2019, 11:08 PM
    What's the best way to achieve this?
    // Transform all instances of this call...
    foo()
    
    // ...into this via a compiler plugin:
    foo("Hello")
    I'm playing around with
    CallResolutionInterceptorExtension
    but wondering if there are any other extensions I should be looking at.
    j
    • 2
    • 10
  • e

    egorand

    12/26/2019, 9:56 PM
    Can anybody point me to the compiler code that checks whether an otherwise illegal identifier (e.g. is a keyword or contains spaces) can be made legal by enclosing it in backticks? In case if it's platform specific, I'm mostly interested in JVM. Thanks!
    s
    • 2
    • 2
  • b

    bjonnh

    12/29/2019, 6:05 AM
    the type was used in an interface, and it looks like that it is this definition that made the compiler barf
    d
    • 2
    • 7
  • c

    Christian Maus

    12/29/2019, 11:20 AM
    I’m running into aproblem with type inference and I’m not sure if this is a known limitation or if I should file a bug. In the snippet below, the type of `does_not_work`is inferred wrong.
    fun <IP, R> (() -> IP).then(f: (IP) -> R): () -> R = { f(this()) }
    
    sealed class Option<out T>
    data class Some<T>(val value: T) : Option<T>()
    object None : Option<Nothing>()
    
    fun maybeNull(): String? = "abcd"
    
    fun <T> toOption(value: T?) = if (value != null) {
        Some(value)
    } else {
        None
    }
    
    fun toStringOption(value: String?) = value?.let { Some(value) } ?: None
    
    //should be inferred to () -> Option<String>, but is inferred to () -> Option<String?>
    val does_not_work: () -> Option<String> = ::maybeNull.then(::toOption)
    
    //type is correct if no generic type is used
    val works: () -> Option<String> = ::maybeNull.then(::toStringOption)
    d
    r
    • 3
    • 2
  • m

    Marc Knaup

    12/31/2019, 8:28 AM
    What is the reason that Kotlin Fir visitors use
    Nothing?
    to denote “no data” rather than
    Unit
    ? https://github.com/JetBrains/kotlin/blob/0e4e5ac287c3836ca4434c2c6f626b9145c284e5/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/visitors/FirVisitorVoid.kt#L128
    d
    • 2
    • 2
  • r

    Rohan Maity

    01/03/2020, 7:28 AM
    Hello I want to make compiler plugin which would be extension of
    all-open
    except that it would contain some extra custom pre-configured annotations for ease
    s
    • 2
    • 5
  • c

    Christian Maus

    01/12/2020, 6:18 PM
    I’m playing around with a GADT example ported from scala and there seems to be be a compiler restriction on kotlin, so that the compiler does not recognize that the resulting type of the expression matches generic type of the case class:
    sealed class Expression<A>
    data class Num(val value: Int) : Expression<Int>()
    data class Bool(val value: Boolean) : Expression<Boolean>()
    data class Add(val a: Expression<Int>, val b: Expression<Int>) : Expression<Int>()
    data class Equals<A>(val first: Expression<A>, val second: Expression<A>) : Expression<Boolean>()
    
    fun <A> eval(e: Expression<A>): A = when (e) {
        is Num -> e.value
        is Bool -> e.value
        is Add -> (eval(e.a) + eval(e.b))
        is Equals<*> -> eval(e.first) == eval(e.second)
    } as A
    
    fun main(args: Array<String>) {
    
        val exp = Equals(
            Equals(
                Num(3),
                Add(
                    Num(1),
                    Num(2)
                )
            ),
            Bool(true)
        )
    
        println(eval(exp))
    }
    Is there a way to get rid of the cast to A?
    d
    r
    +2
    • 5
    • 5
  • z

    zak.taccardi

    01/15/2020, 5:28 PM
    does mixing Java and Kotlin files in the same JVM project affect build speeds negatively? Is it more efficient to be 100% Kotlin? (note: this applies to a JVM project that imports JARs/AARs for Android)
    w
    t
    • 3
    • 2
  • k

    KoxAlen

    01/24/2020, 9:57 AM
    Mmm not sure if this is the proper channel to ask about reified behaviour... I have the following code
    import java.lang.reflect.ParameterizedType
    import java.lang.reflect.Type
    import kotlin.reflect.typeOf
    
    data class Definition(
        val name: String
    )
    
    @ExperimentalStdlibApi
    fun main() {
        val parsed1: List<Definition> = parseGood("")
        val parsed2: List<Definition> = parseBad("")
    }
    
    @ExperimentalStdlibApi
    private inline fun <reified T> parseGood(blob: String): T {
        return readValue(blob)
    }
    
    @ExperimentalStdlibApi
    private inline fun <reified T> parseBad(blob: String): List<T> {
        return readValue(blob)
    }
    
    @ExperimentalStdlibApi
    private inline fun <reified T> readValue(blob: String): T {
        println(typeOf<T>())
        val type = object : TypeHolder<T>() {}.type
        println(type)
        //Do magic with type and return a T
        return listOf(Definition("foo")) as T
    }
    
    open class TypeHolder<T> protected constructor() {
        private var _type: Type
        val type get() = _type
    
        init {
            val superClass = javaClass.genericSuperclass
            _type = (superClass as ParameterizedType).actualTypeArguments[0]
        }
    }
    This prints
    kotlin.collections.List<Definition>
    java.util.List<? extends Definition>
    kotlin.collections.List<Definition>
    java.util.List<? extends T>
    So for
    typeOf
    T is the same in both cases but for the subclass generation one has the signature with the real type and the other one doesn't. Is this a bug, or I'm missing something?
    u
    • 2
    • 3
  • c

    charlesmuchene

    01/27/2020, 9:22 AM
    Any idea while the compiler isn’t able to infer that the
    self-referential
    vararg parameter passed on the constructor is a container and thus the given class is
    constructible
    ? Current fix: Requires I make the parameter type nullable or otherwise suppress this warning.
    d
    d
    • 3
    • 6
  • m

    mp

    01/27/2020, 7:29 PM
    Why would the compiler not allow me to call
    intValue()
    on
    BigDecimal
    ? https://pl.kotl.in/kC_HQAcM2
    e
    • 2
    • 2
  • k

    Kiryushin Andrey

    01/28/2020, 9:01 AM
    I've got a question about safe call operator behavior (
    .?
    ) in Kotlin vs the similar operator in C#. Say we have such classes
    data class Head(val name: String)
        data class Department(val head: Head)
        data class Employee(val department: Department)
    and the variable
    e
    of type
    Employee?
    . Kotlin will refuse to compile an expression
    e?.department.head.name
    complaining that
    head
    property cannot be accessed on an instance of
    Department?
    type. So this expression needs to be rewritten as
    e?.department?.head?.name
    , which in my opinion looks a bit clumsy cause we know in advance that the only thing in this chain that can possibly be null is
    e
    . In C#, on the other hand, this operator discards the whole chain if null is encountered, compiling
    e?.Department.Head.Name
    to
    (e != null) ? e.Department.Head.Name : null
    . Am I missing something or does C# really do a better job in this case? Is this difference in behavior due to the fact that it was hard or impossible to to implement a C#-like behavior given null-aware type system? Or maybe there are some other cases when this Kotlin compiler behavior allows for safer code and therefore is desired? Would like to hear the community as well as JetBrains folks' thoughts on this.
    d
    • 2
    • 4
  • j

    jimn

    01/28/2020, 11:12 AM
    does this have a known solution?
    e: org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: wrong bytecode generated
    d
    • 2
    • 3
  • j

    jimn

    01/28/2020, 11:19 AM
    yeah eliminating kotlintest and incidentally my unit tests has enabled success.
    d
    • 2
    • 10
Powered by Linen
Title
j

jimn

01/28/2020, 11:19 AM
yeah eliminating kotlintest and incidentally my unit tests has enabled success.
d

dmitriy.novozhilov

01/28/2020, 11:20 AM
interesting do you use any compiler plugins from arrow or just library?
j

jimn

01/28/2020, 11:22 AM
i just nuked my groovy gradle and rebooted everything from a clean project's kotlin gradle when this happened, because i wanted to test the eap to see if the result changed over 1-3-61
nothing but kotlin-test is enabled, and coroutines
https://github.com/jnorthrup/columnar/commit/dbcb532152969d08ef64ea0cdf75891efecfb0a5 commit for which kotlin-test can be eliminated along with the dependent tests.
https://youtrack.jetbrains.com/issue/KT-36206
d

dmitriy.novozhilov

01/28/2020, 11:26 AM
Cool, thank you
j

jimn

01/28/2020, 7:08 PM
my problem exists in the unit tests and not kotlintest
https://github.com/jnorthrup/columnar/commit/f55e02834946e06a26825e687b807959c1f1b301#r37005774
View count: 7