Posts in Kotlin (14 found)
Brain Baking 4 days ago

Rendering Your Java Code Less Error Prone

Error Prone is Yet Another Programming Cog invented by Google to improve their Java build system. I’ve used the multi-language PMD static code analyser before (don’t shoot the messenger!), but Error Prone takes it a step further: it hooks itself into your build system, converting programming errors as compile-time errors. Great, right, detecting errors earlier, without having to kick an external process like PMD into gear? Until you’re forced to deal with hundreds of errors after enabling it: sure. Expect a world of hurt when your intention is to switch to Error Prone just to improve code linting, especially for big existing code bases. Luckily, there’s a way to gradually tighten the screw: first let it generate a bunch of warnings and only when you’ve tackled most of them, turn on Error! Halt! mode. When using Gradle with multiple subprojects, things get a bit more convoluted. This mainly serves as a recollection of things that finally worked—feeling of relief included. The root file: The first time you enable it, you’ll notice a lot of nonsensical errors popping up: that’s what that is for. We currently have the following errors disabled: Error Prone’s powerful extendability resulted in Uber picking up where Google left off by releasing NullAway , a plug-in that does annotation-based null checking fully supporting the JSpecify standard . That is, it checks for stupid stuff like: JSpecify is a good attempt at unifying these annotations—last time I checked, IntelliJ suggested auto-importing them from five different packages—but the biggest problem is that you’ll have to dutifully annotate where needed yourself. There are OpenRewrite JSpecify recipes available to automatically add them but that won’t even cover 20% of the cases, as when it comes to manual if null checks and the use of , NullAway is just too stupid to understand what your intentions are. NullAway assumes non-null by default. This is important, because in Java object terminology, everything is nullable by default. You won’t need to add a lot of annotations, but adding has a significant ripple effect: if that’s nullable, then the object calling this object might also be, which means I should add this annotation here and here and here and here and here and… Uh oh. After 100 compile errors, Gradle gives up. I fixed 100 errors, recompiled, and 100 more appeared. This fun exercise lasted almost an entire day until I was the one giving up. The potential commit touched hundreds of files and added more bloat to an already bloated (it’s Java, remember) code base I’ve ever seen. Needless to say, we’re currently evaluating our options here. I’ve also had quite a bit of trouble picking the right combination of plug-ins for Gradle to get this thing working. In case you’d like to give it a go, extend the above configuration with: You have to point NullAway to the base package path ( ) otherwise it can’t do its thing. Note the configuration: we had a lot of POJOs with private constructors that set fields to while they actually cannot be null because of serialisation frameworks like Jackson/Gson. Annotate these with and NullAway will ignore them. If you thought fixing all Error Prone errors was painful, wait until you enable NullAway. Every single statement needs its annotation. OpenRewrite can help, but up to a point, as for more complicated assignments you’ll need to decide for yourself what to do. Not that the exercise didn’t bear any fruit. I’ve spotted more than a few potential mistakes we made in our code base this way, and it’s fun to try and minimize nullability. The best option of course is to rewrite the whole thing in Kotlin and forget about the suffix. All puns aside, I can see how Error Prone and its plug-ins can help catch bugs earlier, but it’s going to come at a cost: that of added annotation bloat. You probably don’t want to globally disable too many errors so is also going to pop up much more often. A difficult team decision to make indeed. Related topics: / java / By Wouter Groeneveld on 25 November 2025.  Reply via email . —that’s a Google-specific one? I don’t even agree with this thing being here… —we’d rather have on every line next to each other —we can’t update to JDK9 just yet —we’re never going to run into this issue —good luck with fixing that if you heavily rely on reflection

0 views
Neil Madden 2 weeks ago

Monotonic Collections: a middle ground between immutable and fully mutable

This post covers several topics around collections (sets, lists, maps/dictionaries, queues, etc) that I’d like to see someone explore more fully. To my knowledge, there are many alternative collection libraries for Java and for many other languages, but I’m not aware of any that provide support for monotonic collections . What is a monotonic collection, I hear you ask? Well, I’m about to answer that. Jesus, give me a moment. It’s become popular, in the JVM ecosystem at least, for collections libraries to provide parallel class hierarchies for mutable and immutable collections: Set vs MutableSet, List vs MutableList, etc. I think this probably originated with Scala , and has been copied by Kotlin , and various alternative collection libraries, e.g. Eclipse Collections , Guava , etc. There are plenty of articles out there on the benefits and drawbacks of each type. But the gulf between fully immutable and fully mutable objects is enormous: they are polar opposites, with wildly different properties, performance profiles, and gotchas. I’m interested in exploring the space between these two extremes. (Actually, I’m interested in someone else exploring it, hence this post). One such point is the idea of monotonic collections, and I’ll now explain what that means. By monotonic I mean here logical monotonicity : the idea that any information that is entailed by some set of logical formulas is also entailed by any superset of those formulas. For a collection data structure, I would formulate that as follows: If any (non-negated) predicate is true of the collection at time t , then it is also true of the collection at any time t’ > t . For example, if c is a collection and c.contains(x) returns true at some point in time, then it must always return true from then onwards. To make this concrete, a MonotonicList (say) would have an append operation, but not insert , delete , or replace operations. More subtly, monotonic collections cannot have any aggregate operations: i.e., operations that report statistics/summary information on the collection as a whole. For example, you cannot have a size method, as the size will change as new items are added (and thus the predicate can become false). You can have (as I understand it) map and filter operations, but not a reduce / fold . So why are monotonic collections an important category to look at? Firstly, monotonic collections can have some of the same benefits as immutable data structures, such as simplified concurrency. Secondly, monotonic collections are interesting because they can be (relatively) easily made distributed, per the CALM principle: Consistency as Logical Monotonicity (insecure link, sorry). This says that monotonic collections are strongly eventually consistent without any need for coordination protocols. Providing such collections would thus somewhat simplify making distributed systems. Interestingly, Kotlin decided to make their mutable collection classes sub-types of the immutable ones: MutableList is a sub-type of List, etc. (They also decided to make the arrows go the other way from normal in their inheritance diagram, crazy kids). This makes sense in one way: mutable structures offer more operations than immutable ones. But it seems backwards from my point of view: it says that all mutable collections are immutable, which is logically false. (But then they don’t include the word Immutable in the super types). It also means that consumers of a List can’t actually assume it is immutable: it may change underneath them. Guava seems to make the opposite decision: ImmutableList extends the built-in (mutable) List type, probably for convenience. Both options seem to have drawbacks. I think the way to resolve this is to entirely separate the read-only view of a collection from the means to update it. On the view-side, we would have a class hierarchy consisting of ImmutableList, which inherits from MonotonicList, which inherits from the general List. On the mutation side, we’d have a ListAppender and ListUpdater classes, where the latter extends the former. Creating a mutable or monotonic list would return a pair of the read-only list view, and the mutator object, something like the following (pseudocode): The type hierarchies would look something like the following: This seems to satisfy allowing the natural sub-type relationships between types on both sides of the divide. It’s a sort of CQRS at the level of data structures, but it seems to solve the issue that the inheritance direction for read-only consumers is the inverse of the natural hierarchy for mutating producers. (This has a relationship to covariant/contravariant subtypes, but I’m buggered if I’m looking that stuff up again on my free time). Anyway, these thoughts are obviously pretty rough, but maybe some inklings of ideas if anyone is looking for an interesting project to work on.

0 views
matklad 3 weeks ago

On Async Mutexes

A short note on contradiction or confusion in my language design beliefs I noticed today. One of the touted benefits of concurrent programming multiplexed over a single thread is that mutexes become unnecessary. With only one function executing at any given moment in time data races are impossible. The standard counter to this argument is that mutual exclusion is a property of the logic itself, not of the runtime. If a certain snippet of code must be executed atomically with respect to everything else that is concurrent, then it must be annotated as such in the source code. You can still introduce logical races by accidentally adding an in the middle of the code that should be atomic. And, while programming, you are adding new s all the time! This argument makes sense to me, as well its as logical conclusion. Given that you want to annotate atomic segments of code anyway, it makes sense to go all the way to Kotlin-style explicit async implicit await. The contradiction I realized today is that for the past few years I’ve been working on a system built around implicit exclusion provided by a single thread — TigerBeetle! Consider compaction, a code that is responsible for rewriting data on disk to make it smaller without changing its logical contents. During compaction, TigerBeetle schedules a lot of concurrent disk reads, disk writes, and CPU-side merges. Here’s an average callback: This is the code ( source ) that runs when a disk read finishes, and it mutates — shared state across all outstanding IO. It’s imperative that no other IO completion mutates compaction concurrently, especially inside that monster of a function . Applying “make exclusion explicit” rule to the code would mean that the entire needs to be wrapped in a mutex, and every callback needs to start with lock/unlock pair. And there’s much more to TigerBeetle than just compaction! While some pairs of callbacks probably can execute concurrently relatively to each other, this changes over time. For example, once we start overlapping compaction and execution, those will be using our GridCache (buffer manager) at the same time. So explicit locking probably gravitates towards having just a single global lock around the entire state, which is acquired for the duration of any callback. At which point, it makes sense to push lock acquisition up to the event loop, and we are back to the implicit locking API! This seems to be another case of two paradigms for structuring concurrent programs . The async/await discussion usually presupposes CSP programming style, where you define a set of concurrent threads of execution, and the threads are mostly independent, sharing a little of data. TigerBeetle is written in a state machine/actor style, where the focal point is the large amount of shared state, which is evolving in discrete steps in reaction to IO events (there’s only one “actor” in TigerBeetle). Additionally, TigerBeetle uses manual callbacks instead of async/await syntax, so inserting an in the middle of critical section doesn’t really happen. Any new concurrency requires introducing an explicit named continuation function, and each continuation (callback) generally starts with a bunch of assertions to pin down the current state and make sure that the ground hasn’t shifted too far since the IO was originally scheduled. Or, as is the case with , sometimes the callback doesn’t assume anything at all about the state of the world and instead carries out an exhaustive case analysis from scratch.

0 views
Kaushik Gopal 1 months ago

Cargo Culting

If you’re a software engineer long enough, you will meet some gray beards that throw out-of-left-field phrases to convey software wisdom. For example, you should know if you’re yak-shaving or bike-shedding , and when that’s even a good thing. A recent HN article 1 reminded me of another nugget – Cargo Culting (or Cargo Cult Programming). Cargo Culting : ritualizing a process without understanding it. In the context of programming: practice of applying a design pattern or coding style blindly without understanding the reasons behind it I’m going to take this opportunity to air one of my personal cargo-culting pet peeves, sure to kick up another storm: Making everything small . When I get PR feedback saying “this class is too long, split this!”, I get ready to launch into a tirade: you’re confusing small with logically small – ritualizing line count without understanding cohesion. You can make code small by being terse: removing whitespace, cramming logic into one-liners, using clever shorthand 2 . But you’ve just made it harder to read. A function that does one cohesive thing beats multiple smaller functions scattered across files. As the parable goes, after the end of the Second World War, indigenous tribes believed that air delivery of cargo would resume if they carried out the proper rituals, such as building runways, lighting fires next to them, and wearing headphones carved from wood while sitting in fabricated control towers. While on the surface amusing, there’s sadness if you dig into the history and contributing factors (value dominance, language & security barriers). I don’t think that’s reason to avoid the term altogether. We as humans sometimes have to embrace our dark history, acknowledge our wrongs and build kindness in our hearts. We cannot change our past, but we can change our present and future. The next time someone on your team ritualizes a pattern without understanding it, you’ll know what to call it. Who comes up with these terms anyway? Now that you’re aware of the term, you’ll realize that the original article’s use of the term cargo-cult is weak at best. In HN style, the comments were quick to call this out.  ↩︎ You know exactly what I’m thinking of, fellow Kotlin programmers.  ↩︎ Now that you’re aware of the term, you’ll realize that the original article’s use of the term cargo-cult is weak at best. In HN style, the comments were quick to call this out.  ↩︎ You know exactly what I’m thinking of, fellow Kotlin programmers.  ↩︎

0 views
Anton Zhiyanov 2 months ago

Go is #2 among newer languages

I checked out several programming languages rankings. If you only include newer languages (version 1.0 released after 2010), the top 6 are: ➀ TypeScript, ➁ Go, ➂ Rust, ➃ Kotlin, ➄ Dart, and ➅ Swift. Sources: IEEE , Stack Overflow , Languish . I'm not using TIOBE because their method has major flaws. TypeScript's position is very strong, of course (I guess no one likes JavaScript these days). And it's great to see that more and more developers are choosing Go for the backend. Also, Rust scores very close in all rankings except IEEE, so we'll see what happens in the coming years.

0 views
James O'Claire 6 months ago

The Trackers and SDKs in ChatGPT, Claude, Grok and Perplexity

Well for a quick weekend recap I’m going to look at which 3rd party SDKs and API calls I can find in the big 4 Android chat apps based. We’ll be using free data from AppGoblin which you can feel free to browse at any of the links below or on tables. Data is collected via de-compiled SDKs and MITM API traffic. Let’s look first at the development tools. These were interesting to me because I had assumed I’d see more of the dynamic JavaScript libraries like React. Instead we see these are all classic Kotlin apps. If you click through the Chat App names you’ll see the more detailed breakdowns of which specific parts of the libraries they’re using like e (in app animations library) or Kotlin Coil Compose or Square’s . Wow, way more than I expected and with quite the variety! I guess it’s enough we can further break these down. As is common now, most apps have more than one analytics tracker in their app. First up let’s recognize Google, it’s across every app in multiple ways. The main one that is used in most apps is the . GMS which is required for both Firebase and Google Play Services. Here’s an example of the measurement SDKs related to this: Next was statsig.com and wow! I was blown away I found this one in 3 of the 4 apps. This looks like a super popular company and I was surprised as I hadn’t heard of them before. Looking around, they look a bit more developer / product focused, but have tons of features and look quite popular. Finally in the analytics section we’ll add the classic segment.com (marketing analytics) and sentry.io (deployment analytics) which get to call OpenAI and Anthropic as it’s clients. It’s always interesting how every company from games to AI end up needed multiple analytics platforms and probably still depend most on their home BI/Backend. Here’s where the money is at. Now SUPER cool is that RevenueCat is now in both OpenAI and Perplexity. RevenueCat helps to use react native updatable web payment / subscription walls so that marketers can change those sections of the apps without needing to do an entire app update. I believe Perplexity is using Stripe, but that could also be a part of their bigger app ecosystem livekit.io ( AppGoblin livekit.io ) is an AI voice platform which is used by OpenAI and Grok. I’m surprised that OpenAI uses this, as they were quite early to the voice game, but perhaps they use this for some deeper custom voice tools. Perplexity has the most interesting third party tools with MapBox and Shopify. I believe MapBox, which delivers mapping tiles, is used for some of Perplexity’s image generation tools like adding circles/lines etc to maps. After seeing Shopify in Perplexity, I realized there wasn’t a Shopify SDK found for OpenAI (despite checking recently). They have been rolling out shopping features as a way to monetize their app, so I am curious if these are just implemented via API or if they were obfuscated well enough to not be found. If you’re still interested, you can also check out the API calls recorded by each app while open. The data is scrubbed, and I’m not sharing the clear text JSONs associated, but you can see some of the endpoints related to the SDKs. If you have further questions about these, or have a specific piece of data (say GPS, or email) that you’d like to check if it is sent along to any of these, just let me know and we can do further research: https://appgoblin.info/apps/com.openai.chatgpt/data-flows https://appgoblin.info/apps/com.anthropic.claude/data-flows If you have feedback please join the https://appgoblin.info Discord, you can find the link on the home page.

0 views
Kaushik Gopal 6 months ago

Configure Karabiner with ease (& Kotlin)

If you’re obsessed with productivity hacks, you’ve probably heard of Karabiner – the ultimate tool for keyboard customization on macOS. But maintaining Karabiner’s config is a pain. In this post I’ll show you a simpler, Kotlin-powered way to wrangle your Karabiner setup, so you don’t have to wrestle with massive, unwieldy JSON. Here’s the gist: Karabiner lets you intercept any keystroke and remap it to… well, pretty much anything. Want to turn Caps Lock into a Hyper key? Make your keyboard launch confetti? It’s all possible. Take the classic usecase: remap Caps Lock to Hyper (⌘⌥⌃⇧) when held, Escape when tapped. That’s easy to do but just scratches the surface. With Karabiner, you can build many more customizations that supercharge your workflow. I was watching a video by Max Stoiber where he uses Raycast 1 & Karabiner. But what struck me was how he customized and maintained his karabiner setup. Karabiner runs off a single config file. But that .json configuration can become unwieldy and difficult to manage, as your rules grow in complexity. For example: I require a ~2700 line for all my hacks. It’s impossible to maintain it as pure json. Max uses TypeScript to maintain his rules -which then compiles down to a .json file- that Karabiner can consume. I really like this approach. If you read my previous blog post , I also ran into a very similar problem and used goku, which in turn used the very esoteric and terse edn format. I like TypeScript over edn but you know what I’d like even better? Kotlin ! So on a ✈️ ride back home, I decided to whip up karabiner-kt . I’m really happy with my solution. Kotlin affords a much more pleasant DSL than most other 2 languages. Here’s what a Karabiner rule looks like in Kotlin : Clean, expressive, and type-safe! Or here’s a fun one. Hold the “O” key and tap “0” for showing the Raycast Confetti: Here’s a slightly more complex modification: If I hold the “f” key and tap j/k, it types out an open and closed bracket respectively. The repo is open source , so feel free to take a look and customize. Getting started is easy. Here’s how to set it up from scratch: Now every time you want to run the configurator: All your customizations live in Rules.kt . My own config is about 300 lines of Kotlin. Compare that to the monstrous 2736-line JSON it generates. 😅 You should start with a really simple setup and build your Rules over time. Give it a shot and let me know if you run into issues ! my launcher of choice.  ↩︎ maybe TypeScript allows this but I understand Kotlin better  ↩︎ my launcher of choice.  ↩︎ maybe TypeScript allows this but I understand Kotlin better  ↩︎

0 views
baby steps 8 months ago

Rust in 2025: Language interop and the extensible compiler

For many years, C has effectively been the “lingua franca” of the computing world. It’s pretty hard to combine code from two different programming languages in the same process–unless one of them is C. The same could theoretically be true for Rust, but in practice there are a number of obstacles that make that harder than it needs to be. Building out silky smooth language interop should be a core goal of helping Rust to target foundational applications . I think the right way to do this is not by extending rustc with knowledge of other programming languages but rather by building on Rust’s core premise of being an extensible language. By investing in building out an “extensible compiler” we can allow crate authors to create a plethora of ergonomic, efficient bridges between Rust and other languages. When it comes to interop… When it comes to extensibility… In my head, I divide language interop into two core use cases. The first is what I call Least Common Denominator (LCD), where people would like to write one piece of code and then use it in a wide variety of environments. This might mean authoring a core SDK that can be invoked from many languages but it also covers writing a codebase that can be used from both Kotlin (Android) and Swift (iOS) or having a single piece of code usable for everything from servers to embedded systems. It might also be creating WebAssembly components for use in browsers or on edge providers. What distinguishes the LCD use-case is two things. First, it is primarily unidirectional—calls mostly go from the other language to Rust. Second, you don’t have to handle all of Rust. You really want to expose an API that is “simple enough” that it can be expressed reasonably idiomatically from many other languages. Examples of libraries supporting this use case today are uniffi and diplomat . This problem is not new, it’s the same basic use case that WebAssembly components are targeting as well as old school things like COM and CORBA (in my view, though, each of those solutions is a bit too narrow for what we need). When you dig in, the requirements for LCD get a bit more complicated. You want to start with simple types, yes, but quickly get people asking for the ability to make the generated wrapper from a given language more idiomatic. And you want to focus on calls into Rust, but you also need to support callbacks. In fact, to really integrate with other systems, you need generic facilities for things like logs, metrics, and I/O that can be mapped in different ways. For example, in a mobile environment, you don’t necessarily want to use tokio to do an outgoing networking request. It is better to use the system libraries since they have special cases to account for the quirks of radio-based communication. To really crack the LCD problem, you also have to solve a few other problems too: Obviously, there’s enough here to keep us going for a long time. I think the place to start is building out something akin to the “serde” of language interop: the serde package itself just defines the core trait for serialization and a derive. All of the format-specific details are factored out into other crates defined by a variety of people. I’d like to see a universal set of conventions for defining the “generic API” that your Rust code follows and then a tool that extracts these conventions and hands them off to a backend to do the actual language specific work. It’s not essential, but I think this core dispatching tool should live in the rust-lang org. All the language-specific details, on the other hand, would live in crates.io as crates that can be created by anyone. The second use case is what I call the deep interop problem. For this use case, people want to be able to go deep in a particular language. Often this is because their Rust program needs to invoke APIs implemented in that other language, but it can also be that they want to stub out some part of that other program and replace it with Rust. One common example that requires deep interop is embedded developers looking to invoke gnarly C/C++ header files supplied by vendors. Deep interop also arises when you have an older codebase, such as the Rust for Linux project attempting to integrate Rust into their kernel or companies looking to integrate Rust into their existing codebases, most commonly C++ or Java. Some of the existing deep interop crates focus specifically on the use case of invoking APIs from the other language (e.g., bindgen and duchess ) but most wind up supporting bidirectional interaction (e.g., pyo3 , [npapi-rs][], and neon ). One interesting example is cxx , which supports bidirectional Rust-C++ interop, but does so in a rather opinionated way, encouraging you to make use of a subset of C++’s features that can be readily mapped (in this way, it’s a bit of a hybrid of LCD and deep interop). I want to see smooth interop with all languages, but C and C++ are particularly important. This is because they have historically been the language of choice for foundational applications, and hence there is a lot of code that we need to integrate with. Integration with C today in Rust is, in my view, “ok” – most of what you need is there, but it’s not as nicely integrated into the compiler or as accessible as it should be. Integration with C++ is a huge problem. I’m happy to see the Foundation’s Rust-C++ Interoperability Initiative as well a projects like Google’s crubit and of course the venerable cxx . The traditional way to enable seamless interop with another language is to “bake it in” i.e., Kotlin has very smooth support for invoking Java code and Swift/Zig can natively build C and C++. I would prefer for Rust to take a different path, one I call the extensible compiler . The idea is to enable interop via, effectively, supercharged procedural macros that can integrate with the compiler to supply type information, generate shims and glue code, and generally manage the details of making Rust “play nicely” with another language. In some sense, this is the same thing we do today. All the crates I mentioned above leverage procedural macros and custom derives to do their job. But procedural macrods today are the “simplest thing that could possibly work”: tokens in, tokens out. Considering how simplistic they are, they’ve gotten us remarkably, but they also have distinct limitations. Error messages generated by the compiler are not expressed in terms of the macro input but rather the Rust code that gets generated, which can be really confusing; macros are not able to access type information or communicate information between macro invocations; macros cannot generate code on demand, as it is needed, which means that we spend time compiling code we might not need but also that we cannot integrate with monomorphization. And so forth. I think we should integrate procedural macros more deeply into the compiler. 2 I’d like macros that can inspect types, that can generate code in response to monomorphization, that can influence diagnostics 3 and lints, and maybe even customize things like method dispatch rules. That will allow all people to author crates that provide awesome interop with all those languages, but it will also help people write crates for all kinds of other things. To get a sense for what I’m talking about, check out F#’s type providers and what they can do. The challenge here will be figuring out how to keep the stabilization surface area as small as possible. Whenever possible I would look for ways to have macros communicate by generating ordinary Rust code, perhaps with some small tweaks. Imagine macros that generate things like a “virtual function”, that has an ordinary Rust signature but where the body for a particular instance is constructed by a callback into the procedural macro during monomorphization. And what format should that body take? Ideally, it’d just be Rust code, so as to avoid introducing any new surface area. So, it turns out I’m a big fan of Rust. And, I ain’t gonna lie, when I see a prominent project pick some other language, at least in a scenario where Rust would’ve done equally well, it makes me sad. And yet I also know that if every project were written in Rust, that would be so sad . I mean, who would we steal good ideas from? I really like the idea of focusing our attention on making Rust work well with other languages , not on convincing people Rust is better 4 . The easier it is to add Rust to a project, the more people will try it – and if Rust is truly a better fit for them, they’ll use it more and more. This post pitched out a north star where How do we get there? I think there’s some concrete next steps: Well, as easy as it can be.  ↩︎ Rust’s incremental compilation system is pretty well suited to this vision. It works by executing an arbitrary function and then recording what bits of the program state that function looks at. The next time we run the compiler, we can see if those bits of state have changed to avoid re-running the function. The interesting thing is that this function could as well be part of a procedural macro, it doesn’t have to be built-in to the compiler.  ↩︎ Stuff like the tool attribute namespace is super cool! More of this!  ↩︎ I’ve always been fond of this article Rust vs Go, “Why they’re better together” .  ↩︎

0 views
Kaushik Gopal 8 months ago

AI & Software Engineering: Time for optimism

Many software engineers (myself included, not long ago) have been anxious about AI replacing our jobs. It’s a natural concern. But I’ve been diving deep into using AI for software engineering. I’ve had a realization: We should stop worrying about job losses. Start seeing AI as a tool to make us more valuable. The key? Use AI for learning, not just code generation. Relying solely on AI for code generation is the trap. The truly valuable skill is judgment 1 . The judgment to decide what to build and how to build it 2 . AI excels at the actual building (code generation). Embrace this. It’s inevitable. Most future code will be written|generated with AI. The good news? AI is amazing for learning. I think back to my undergraduate days. A concept didn’t click? It meant a bike ride to the library. I’d search for textbooks, pull references, and try to piece it all together. Assuming the books were even available. Guess what I often did? I didn’t bother. Today, I’d just pull up my phone and look it up. The internet was a game-changer for students. AI is the next game-changer for all of us willing to learn. A more recent example : I’m doing an upcoming Fragmented episode on an article that my friend Colin 3 wrote on Coroutine Dispatcher use. I understood about 75% on the first pass. But a few gaps remained, especially around how they related to Coroutine fundamentals. I needed to brush up. Without AI, my process would have been: What would have taken 2-3 hours took about 30 minutes 4 . And I understood the concepts better . AI is your opportunity to level up. Learn faster. Position yourself to make the critical decisions: “what” and “how” to build. This is where software engineers, especially new ones , should focus. You don’t have the luxury of years of experience that some of us “senior” engineers had. AI lets you catch up, fast. Yes, the bar will be raised. Getting into the field won’t be as easy. But for those willing to learn and adapt, AI is an incredible advantage. Embrace it. My friend Vinay and someone who’s also working very closely with AI for software development eloquently mentioned this in ep #257 .  ↩︎ For now, AI isn’t as good as this. It will catch up but my bet is it’ll take longer to close this specific gap.  ↩︎ Yes, that one .  ↩︎ If i’m being honeset, a lart part of the time was spent thinking about how I’d connect the dots to write this blog post.  ↩︎ Open a bunch of articles and docs. Read each one thoroughly. Synthesize the information mentally. Connect the dots with the article. Time: 2-3 hours, plus significant mental bandwidth. Uploaded Colin’s article and official Kotlin docs to Google’s Notebook LM . Asked targeted questions, addressing my knowledge gaps directly . Time: 30 minutes to reach a firm understanding. My friend Vinay and someone who’s also working very closely with AI for software development eloquently mentioned this in ep #257 .  ↩︎ For now, AI isn’t as good as this. It will catch up but my bet is it’ll take longer to close this specific gap.  ↩︎ Yes, that one .  ↩︎ If i’m being honeset, a lart part of the time was spent thinking about how I’d connect the dots to write this blog post.  ↩︎

0 views
Takuya Matsuyama 9 months ago

Supporting iOS Share Extensions & Android Intents on React Native

Hi, it's Takuya, the solo developer of Inkdrop , a note-taking app designed for software developers. Recently, I enabled a web clipper feature on the mobile app for both iOS and Android. I'd like to share how I implemented the logic to receive a shared URL in React Native. My mobile app is a 'bare' React Native project (not using Expo), because I started it more than seven years ago—well before Expo was a thing. If you use Expo, check out MaxAst/expo-share-extension . One challenge for React Native apps is how to display a custom view in an iOS share extension. Because a share extension and its containing app are separate processes, you can’t directly share instances or data, and you need a method of communication, such as storing files in the same App Group. Typically, apps send shared content to their servers, e.g., via REST APIs, and then load it in the containing app upon launch. However, because Inkdrop uses end-to-end encryption with its servers, supporting that same encryption flow in the share extension would be complicated. Instead, I decided to redirect to the containing app immediately after the user selects Inkdrop from the share sheet. This approach is used by other apps like Bluesky, which is also built with React Native. It appears that Apple permits launching a containing app from a share extension—there’s a Stack Overflow discussion about it. I also found a promising library called react-native-receive-sharing-intent , which provides a useful guide on setting up a share extension for iOS and handling shared Intents on Android. Instead of installing it, I followed the project setup steps from the documentation and wrote my own native code, because I prefer minimal dependencies and I’m comfortable tweaking native code. Here is a demo of Inkdrop: 0:00 / 0:04 1× In Xcode, go to File → New → Target... , then choose “Share Extension”. Give it a descriptive name (for example, "InkdropShare")—this name will appear in the share sheet. In my case, the app only accepts URLs, so the looks like this: After creating the extension, you should see its icon in the iOS share sheet. Xcode automatically creates a Swift file for the view controller ( ). This class is loaded by the share extension. To redirect to the containing app, I tested a snippet from the documentation, but it didn’t work. I ended up referring to Bluesky’s implementation and adapted the code: This code opens a deep link to the main app with the URI . React Native has built-in support for deep linking: For example: Android uses a concept called Intents to communicate between apps. If you have native Android experience, you’ll be familiar with it. Here, you’ll make your app capable of receiving shared URLs via Intents. Deep linking is not required to handle incoming Intents. Add an in your manifest to tell Android your app can accept text data from other apps: The library uses deep linking to detect incoming Intents, but this approach didn’t work in my project. The React Native module doesn’t emit a event for . Inside , you can see that it only handles or . You can manually detect in Kotlin (or Java) and pass the data to React Native. Here, lets you emit custom events to the JavaScript side. Once the event is dispatched, you can listen for it in your React code. Create a small module for listening to the custom event: Then, in your app: That’s the gist of how I added a share extension on iOS and handled on Android for my React Native app. I hope you find it helpful! Check out my app if you're looking for a Markdown note-taking app :) React Native Docs: Linking

0 views
Ivan Sagalaev 2 years ago

On Kotlin

I've been writing code in Kotlin on and off over a few months, and I think I'm now at this unique stage of learning something new when I already have a sense of what's what, but not yet so far advanced so I don't remember beginner's pain points. Here's a dump of some of my impressions, good and bad. We were not out to win over the Lisp programmers; we were after the C++ programmers. We managed to drag a lot of them about halfway to Lisp. — Guy Steele Kotlin drags Java programmers another half of the rest of the way. That is to say, Kotlin doesn't feel like a real functional-first language. It's still mostly Java with all its imperativism, mutability and OO, but layered with some (quite welcome) syntactic sugar that makes it less verbose and actually encourages functional style. Where it still feels mostly Java-ish is when you need to work with Java libraries. Which is most of the time, since the absolutely transparent Java interop doesn't make writing Kotlin-flavored libraries a necessity. For starters, you don't have to put everything in classes with methods any more. Plain top-level functions are perfectly okay. You also don't need to write/generate a full-blown class if what you really need is a struct/record. Instead you just do: These have some handy features (like comparability) implemented out of the box, which is nice. And then you can pass them to functions as plain arguments, without necessarily having to make them methods on those argument's classes. Like other newer languages (Swift, Rust) Kotlin allows you to add your own methods to existing classes, even to built-in types. They are neatly scoped to whatever package they're defined in, and don't hijack the type for the entirety of the code in your program. The latter is what happens when you add a new method to a built-in class dynamically in Ruby, and as far as I know, it's a constant source of bad surprises. It doesn't require any special magic. Just keep in mind that is not really different from , only the name of the first parameter is going to be , and it's going to be available implicitly. This, I think, is actually a big deal, becasue looser coupling between types and functions operating on them pushes you away from building rigid heirarchies. And by now I believe most people have realized that inheritance doesn't scale. So these days the only real value in having over is the ability to compose functions in the natural direction: … as opposed to Yes, I know your Haskell/OCaml/Clojure have their own way of doing it. Good. Kotlin has chaining. Kotlin uses and for declaring local data as immutable and mutable, respectively. is encouraged to be used by default, and the compiler will yell at you if you use without actually needing to mutate the variable. This is very similar to Rust's and . Unfortunately however, Kotlin doesn't enforce immutability of a class instance inside its methods, so it's still totally possible to do: … and have internal state changed unpredictably. Kotlin is another new language adopting "everyhing is an expression" paradigm. You can assign the result of, say, an statement to a variable or it. This plays well with a shortened syntax for functions consisting of a single expression, which doesn't involve curly braces and the keyword: You still need in imperative functions and for early bail-outs. This is all good, I don't know of any downsides. I think Kotlin has easily the best syntax for nameless in-place functions out of all languages with curly braces: You put the body of the function within , no extra keywords or symbols required. If it has one argument (which is very common), it has an implicit short name, . This one is really cool: if the lambda is the last argument of the accepting function, you can take it outside the parentheses, and if there are no other arguments, you can omit the parentheses altogether. So filtering, mapping and reducing a collection looks like: Note the absence of after the first two functions. The line with is more complicated because it does have an extra argument, an initial value, which has to go into parentheses, and it also has a two-argument lambda, so it needs to name them. Many times you can get away with not inventing a name for another temporary variable: takes the object on which it was called ( in this case), passes it as a single argument to its lambda, where you can use it as, well, , and then returns whatever was returned from the lambda. This makes for succinct, closed pieces of code which otherwise would either bleed their local variables outside the scope, or require a named function. This reminds me of Clojure's , and Kotlin also has its own idiom similar to which is a variant that only works when the value is not : If the result of is the operator would safely short-cirquit the whole thing and not call the block. Speaking of , it's actually one of no fewer than five slight variations of the same idea. They vary by which name the object is passed inside the lambda block, and by what it returns, the object itself or the result of the lambda. Here they are: Technically, you can get by with only ever using , because you can always return explicitly, and the difference between and is mostly cosmetic: sometimes you can save more characters by omitting typing , sometimes you still need it to avoid things like , so you switch to using . The real reason for all these variations is they're supposed to convey different semantics . In practice I would say it creates more fuss than it helps, but it may be just my lack of habit. And no, I didn't forget about the fifth one, , which is just a variant of , but you pass the object in parentheses instead of putting it in front of a dot: I can only probably justify its existence by a (misplaced) nostalgia for a similar from Pascal and early JavaScript. And there's a reason nobody uses it anymore: the implicit was a reliable source of hard to spot bugs. By the way, this sudden language complexity is something that Lisps manage to avoid by simply not having the distinction between "functions" and "methods", and always returning the last expression from a form. "An elegant weapon for a more civilized age", and all that :-) That one caught me off guard. Turns out there's a difference on what kind of value you call , and such. Calling them on a does not produce a lazy sequence, it actually produce a concrete list. If you want a lazy result you should cast a concrete collection to first: That's one more gotcha to be aware of if you want to avoid allocating memory for temporary results at every step of your data transformations. In Python, tuples are a workhorse as much as dicts and lists. One of their underappreciated properties is their natural orderability : as long as corresponding elements of two tuples are comparable with each other, tuples are also comparable, with leftmost elements being the most significant, so you have: This is tremendously convenient when sorting collections of custom elements, because you only need to provide a function mapping your custom value to a tuple: Kotlin doesn't have tuples. It has pairs , but they aren't orderable and, well, sometimes you need three elements. Or four! So when you want to compare custom elements you have two options: Define comparability for your custom class. Which you do at the class declaration, way too far away from the place where you're sorting them. Or it may not work for you at all if you need to sort these same elements in more than one way. Define a comparator function in place. Kotlin lambdas help here, but since it needs to return a -1/0/1, it's going to be sprawling and repetitive: for all elements, subtract one from another, check for zero, return if not, move to the next element otherwise. Bleh… It's probably to widespread type inference that we owe the resurgence in popularity of typed languages. It's what makes them palatable. But implementations are not equally capable across the board. I can't claim a lot of cross-language experience here, but one thing I noticed about Kotlin is that it often doesn't go as far as, say, Rust in figuring out what is it that you meant. For example, Kotlin can't figure out the type of an item of an initially empty list based on what data you're adding to it: Rust does this just fine: It's a contrived example, but in paractice I also had stumbled against Kotlin's inability to look into how the type is being used later. This is not a huge problem of course… I'm going to bury the lead here and first give you two examples that look messy (to me) before uncovering the True Source of Evil. The first thing are and modifiers for type parameters. There is a long detailed article about them in the docs about generics which I could only sort of understand after the third time I read it. It all has to do with trying to explain to the compiler the IS-A relationship between containers of sub- and supertypes. Like could be treated as if you only read items from it, but you obviously can't write a random into it. Or something… The second example is about extension methods (those that you define on some third-party class in your namespace) that can't be virtual . It may not be immediately apparent why, until you realize that slapping a method on a class is not the same as overriding it in a descendant, but is simply a syntactic sugar for . So when you call it doesn't actually look into the VMT of , it looks for a free-standing function in a local namespace. You put the body of the function within , no extra keywords or symbols required. If it has one argument (which is very common), it has an implicit short name, . This one is really cool: if the lambda is the last argument of the accepting function, you can take it outside the parentheses, and if there are no other arguments, you can omit the parentheses altogether. takes the object as , returns the object takes the object as , returns the result of the block takes the object as , returns the object takes the object as , returns the result of the block Define comparability for your custom class. Which you do at the class declaration, way too far away from the place where you're sorting them. Or it may not work for you at all if you need to sort these same elements in more than one way. Define a comparator function in place. Kotlin lambdas help here, but since it needs to return a -1/0/1, it's going to be sprawling and repetitive: for all elements, subtract one from another, check for zero, return if not, move to the next element otherwise. Bleh…

0 views
Ivan Sagalaev 4 years ago

New pet project

So anyway, I'm making a shopping list app for Android. As I understand, "shopping list" is something of a hello-world exercise of Android development, which may explain why there are so many rudimentary ones in Google Play. Only in my case I actually need one, and I know exactly what I want from it. See, for the past 10 years or so I've been in charge of food supply in our family, which includes everything from grocery shopping logistics, to cooking, to arranging dishes in the dishwasher. And the app is an essential part of the first stage of that chain. Up until recently I used Out of Milk , which someone suggested me a long time ago, and at that time it was probably the best choice. I remember being quite happy to pay for a full version. Over time though it got a little bloated in ways I didn't need and a little neglected in places I cared about. The UI got very "traditional", requiring fiddly unnecessary motions for core functionality. Here's the short list of its wrongs I still remember: Start-up time of several seconds, sometimes overflowing into dozens. I believe my 4-year old phone should be perfectly able to load a shopping list in sub-second time. Adding an item when it's already on the list results in two identical items on the list. (Yes, really.) Auto suggest when adding an item has whatever ordering and limits the amount of displayed results. This meant I could never get "Tomatoes" in there, as they were buried under "Roma tomatoes", "Cherry tomatoes", and a few others with no way to scroll to it. Tiny click target to check an item off the list. I was constantly fat-fingering around those and getting into a different screen. Checking an item off the list puts it into another list below the main one, which you either have to empty all the time, or end up with a huge scroll height. As I understand, the idea was that you could uncheck the items from there to put them back on the list, but that's unrealistic with my catalog of ~ 150 items. "Smart" categorization kept inventing excessively detailed categories leading to several one-item categories clogging up the list. Sometimes unsuccessful synchronization would "forget" added items on the list. Which is funny because I didn't have anything to synchronize with! I probably could spend some time on searching for an app that'd suit me better, but… Look, I'm a programmer. Writing code is what I do! And I wanted to play with Android development since forever, and the recent exposure to Kotlin gave me all the reasons I didn't really need in the first place :-) Here's a laundry list of what I want from a shopping list: Automatic ordering based on the order in which I buy things. I've had this idea ever since I was using Out Of Milk , because ordering manually sucks, and it feels like something computers should be able to do well, right? However it's really not trivial to implement, if you think of it. So it was my main challenge and a trigger to actually start the project. Fuzzy search for suggested items. I'm used to typing 3-4 characters in my Sublime Text to go to every file or identifier in a project. I want the same service here. Smart sorting of suggested items. It could take into account closeness of matching, frequency and recency of buying. Multiple lists with separate histories. Different stores have different order of aisles, and I buy different things in them. A single list won't cut it. Renaming and annotating items . I get annoyed by typos and spelling errors, I want to correct them. And sometimes I want to add a short note to an item (like a particular brand of cheese, or a reminder that I need two cartons of milk this time). Color-coded categories , to give visual aid in scanning what otherwise would be a plain list of strings. They don't have to be terribly detailed. Less of buttons, check boxes and dialogs. I want to interact with the content itself as much as possible. Swiping items off the list instead of clicking a checkbox. Having lists themselves in a carousel, instead of choosing their names from a , etc. Oh, and no settings, if I can get away with it! Undo . It's really annoying to accidentally swipe off something covered by your thumb only to realize it's not what you intended, and now you have no clue what it was. GPS pinning . This is one aspirational feature I'll probably tackle last, if ever. I want to pin a list to a particular geo location, so the app would automatically select it when I'm at this store again. Also, no tracking, ads or other such bullshit. Should be self-explanatory :-) Not having some ugly API SDK making network calls at startup should really help with performance. I actually first started working on it at the end of 2019 and made good progress into 2020… but then something got in the way. Yeah… Anyway, after making an effort to restart the project I'm making good progress again and actually feel really happy about it all! About a month ago I started dogfooding the app and was able to deleted Out Of Milk from my phone (So long and thanks for all the fish!) I've got the first five features mostly done, but there's nothing like actually using it that keeps showing me various edge cases I could never think about. I love this process :-) Crucially, I can now add "Tomatoes" by just typing "t", "m" — and have them as the first suggestion. The app looks pretty rudimentary, as you'd expect at this stage. But really, this time I want to not just fool around and dump the code somewhere in the open, I actually want to make a finished, sellable product out of it. Going to be a fun adventure! (Technically, me and my wife already tried selling my shareware tools at some time in the previous century, but we managed to only sell about two copies, so it doesn't count.) Start-up time of several seconds, sometimes overflowing into dozens. I believe my 4-year old phone should be perfectly able to load a shopping list in sub-second time. Adding an item when it's already on the list results in two identical items on the list. (Yes, really.) Auto suggest when adding an item has whatever ordering and limits the amount of displayed results. This meant I could never get "Tomatoes" in there, as they were buried under "Roma tomatoes", "Cherry tomatoes", and a few others with no way to scroll to it. Tiny click target to check an item off the list. I was constantly fat-fingering around those and getting into a different screen. Checking an item off the list puts it into another list below the main one, which you either have to empty all the time, or end up with a huge scroll height. As I understand, the idea was that you could uncheck the items from there to put them back on the list, but that's unrealistic with my catalog of ~ 150 items. "Smart" categorization kept inventing excessively detailed categories leading to several one-item categories clogging up the list. Sometimes unsuccessful synchronization would "forget" added items on the list. Which is funny because I didn't have anything to synchronize with! Automatic ordering based on the order in which I buy things. I've had this idea ever since I was using Out Of Milk , because ordering manually sucks, and it feels like something computers should be able to do well, right? However it's really not trivial to implement, if you think of it. So it was my main challenge and a trigger to actually start the project. Fuzzy search for suggested items. I'm used to typing 3-4 characters in my Sublime Text to go to every file or identifier in a project. I want the same service here. Smart sorting of suggested items. It could take into account closeness of matching, frequency and recency of buying. Multiple lists with separate histories. Different stores have different order of aisles, and I buy different things in them. A single list won't cut it. Renaming and annotating items . I get annoyed by typos and spelling errors, I want to correct them. And sometimes I want to add a short note to an item (like a particular brand of cheese, or a reminder that I need two cartons of milk this time). Color-coded categories , to give visual aid in scanning what otherwise would be a plain list of strings. They don't have to be terribly detailed. Less of buttons, check boxes and dialogs. I want to interact with the content itself as much as possible. Swiping items off the list instead of clicking a checkbox. Having lists themselves in a carousel, instead of choosing their names from a , etc. Oh, and no settings, if I can get away with it! Undo . It's really annoying to accidentally swipe off something covered by your thumb only to realize it's not what you intended, and now you have no clue what it was. GPS pinning . This is one aspirational feature I'll probably tackle last, if ever. I want to pin a list to a particular geo location, so the app would automatically select it when I'm at this store again. Also, no tracking, ads or other such bullshit. Should be self-explanatory :-) Not having some ugly API SDK making network calls at startup should really help with performance.

0 views
Ivan Sagalaev 4 years ago

Status update: 2020

Pretty sure I'm in the minority on this one, but this year was actually a good one for me! One of the best in a while, to be honest… There's nothing good about this, and we all could do without it. But even then, being introverted probably helped me to fare better than most with the whole sitting at home thing. Also, nobody of my family and friends had died or had any severe health problems. So I'm filing it under the "could be worse" category. Unlike many, many people who lost their jobs this year I actually got a new one. I was lucky enough to wrap up my interviewing and even do an onboarding week in New York in the first week of March, right before the quarantine. I distinctly remember a conversation with the manager of my favorite breakfast place in NY about the virus: she was worried, and I was completely dismissive about the severity of the thing, saying that it's going to be gone soon like another flu, and the probability to catch it was really low. Yeah… Didn't take long to realize how wrong I was! So anyway this is now my third job in a row where I'm working remotely from West coast on a New York based company :-) This time it's Datadog . Dealing with Python, TypeScript, React, Kubernetes, etc. Nothing out of the ordinary. But going from a household with no income to having two people with income obviously sweetens the deal quite a lot! It was a long process, but now it is fair to say that maintenance of the library is finally out of my hands. I still own the domain name and do occasional merges and fixes to the site code which Josh asks me to, but I'm so very glad the project hasn't died with me losing the interest! This is something that makes me really, really happy. After quite a few years of hiatus I made enough time to go back to programming something for myself. Not nearly as regular as I'd like to, but it is happening, and it's immensely satisfying! This time it's an Android app ( in Kotlin , of course), and the one I wanted to make for myself for more than 5 years: a shopping list. It may sound totally lame but I don't care :-) And I'll definitely blog about it in some more details. One thing that made it possible is that our school has smoothed out the remote learning process enough so that me and my wife don't have to babysit our 3-rd grader through the day. She does everything mostly by herself, so we have time to actually work during the day and I, consequently, have time to do other stuff in the evenings. This was the best year ever, literally! After recovering from yet another of innumerable injuries in the beginning of the year I'm back to running fast, and setting personal bests on both 6 and 13 miles. This is probably in part due to the new running shoes , too. We used to play board games a lot with different friends and colleagues back when we lived in Moscow, but after everyone moved everywhere we pretty much only played once a year when we visited our oldest friends for the New Year celebration. But this year we realized our kid is old enough to be interested in board games, so we went big on that and dedicated a whole room to it! We dug out the old games we hauled with us from Russia and we bought some new ones. And now we have some family fun most nights. I have to say, our girl is scary good at Munchkin!

0 views
SerCe 8 years ago

Fantastic DSLs and where to find them

The article discusses how Kotlin can create internal languages and how developers can use this feature to write powerful domain-specific languages for manipulating untyped data structures in a typed manner.

0 views