www.beust.com Open in urlscan Pro
2606:4700:3034::ac43:a693  Public Scan

Submitted URL: http://beust.com/
Effective URL: https://www.beust.com/weblog/
Submission: On May 04 via manual from US — Scanned from DE

Form analysis 1 forms found in the DOM

GET https://www.beust.com/weblog/

<form method="get" id="searchform" action="https://www.beust.com/weblog/"> <input type="text" name="s" id="searchbox" class="searchfield" value="Search" onfocus="if(this.value == 'Search') {this.value = '';}"
    onblur="if (this.value == '') {this.value = 'Search';}"> <input type="submit" value="Go" class="searchbutton"></form>

Text Content

OTAKU – CEDRIC'S BLOG

THOUGHTS ON SOFTWARE ENGINEERING


 * Home
 * About
 * Contact Us


VIEWING THE WORLD WITH MATHEMATICS

Feb 9

Posted by Cedric in Uncategorized | Comments off

I have a very complex relationship with math. I minored in math but I was really
bad at it, and my classmates were regularly running rings around me. My mind was
simply not a good fit for it. Then I switched to a computer science major, and
everything changed.

But even back then, I realized that math was special. It’s so much more
painfully obvious to me now, but it took a while to get there.

You might know the joke about this math student asking their math teacher “Will
I ever use this in my life, ever??”.

And the math teacher responding “Your smart and successful classmates will”.

Such a slap in the face.

But there is a lot of truth to that joke. Math teaches you a lot of things that
are not directly applicable to the real world, but that is its purpose, and what
makes it so important. Sure, you take a math class, don’t understand a word of
it, and drop out. Understandable. But how many other people do the same thing as
you? 99% of the class? What do you think about this? Don’t you want to be one of
these special cases, one that doesn’t give up easily, one that keeps trying when
everyone else gave up?

Math is like jogging. There is no sport that requires you to jog at a medium
pace for a half hour. But then, why do the best athletes in the world go jogging
several times a week?

Because it trains your body and prepares it for bigger challenges. And that’s
exactly what math does. It shapes your brain, extends it, challenges it, and
prepares it to view the world in abstract ways that very, very, VERY, few people
see.

My kids regulary ask me this very same question, whether I use math in my daily
life. And my response is unwaveringly “All the time”. I use math when I drive,
in traffic, when I read email, when I watch or practice sport, when I look at
the layout of my house or whenever I write code. It’s a constant presence.

Math helps me solidify those natural pattern seeking recognition skills that
evolutional biology has wired in my brain for tens of thousands of years. I
notice things, put them together, draw conclusions, derive decisions.

It’s very hard to explain and put into words, but math is probably the most
important thing I ever studied in my entire life, and I can’t wait to learn more
of it.


IT’S CALLED DEPENDENCY “INJECTION” FOR A REASON

May 11

Posted by Cedric in Uncategorized | 2 Comments

It’s fascinating to me that so many years after the concept of Dependency
Injection (DI) became popular, so many people still don’t seem to understand
what it is and why it’s such a fundamental idea that transcends languages.

> “I already have Dependency Injection, it’s called ‘passing parameters'”
> 
> – Pretty much everyone in a discussion about Dependency Injection

No, passing dependencies as parameters is not DI and it’s actually a terrible
way of building software. Let’s explore why.


DEPENDENCY INJECTION, THE WRONG WAY

Let’s imagine a typical application that starts at main() and then ends up
calling a function you are working on, let’s call it “sendPacket()“:

fun main() {
  f()
}
fun f() {
  g()
}
fun g() {
  sendPacket(packet)
}
fun sendPacket(packet: Packet) { ... }

Next, you decide you want to log whenever a packet is sent, so you need a
logger. The traditional, non DI (and wrong) way to do this would be to
instantiate this logger as high as possible (so it can be used by other
functions) and then passed down:

fun main() {
  val logger = Logger()
  f(logger)
}
fun f(logger: Logger) {
  g(logger)
}
fun g(logger: Logger) {
  sendPacket(packet, logger)
}
fun sendPacket(packet: Packet, logger: Logger) { ... }

Here are some things that are immediately apparent in this new version of the
code:

 * By adding this dependency to sendPacket(), I have impacted all the callers,
   who now have to pass a logger even though they never need one themselves.
 * Anyone who wants to call sendPacket() now needs to find such a logger, even
   though all they want to do is send a packet.
 * sendPacket() is now exposing private details to the outside world, its
   encapsulation is now completely broken.
 * This approach will never scale. Imagine a code base of millions of lines of
   code and the effect that adding a logger to one function will cause on the
   entire code base.

A few quick observations before we dive deeper:

 * It is not the purpose of this article to analyze why being able to create
   these instances differently based on the deployment environment (e.g.
   production, testing, staging, benchmarking, etc…) is desirable.
 * This problem statement generalizes to classes as well, except that you would
   pass these dependencies in the constructor with the same devastating effect.
 * The monadic approach to this problem (using Reader) suffers from this exact
   problem (on top of introducing monads, which create even more issues).


A BETTER APPROACH

Having established that passing dependencies as parameters is not a good idea,
what are our options?

Here are a couple:

 * Make the dependencies global variables.
   We all know how bad an idea that is, but while it turns the code into an
   ungodly pile of spaghetti, it does address the encapsulation breaking aspect:
   functions can add dependencies to their implementations without impacting
   callers since these dependencies are no longer passed as parameters.
 * Find a way to “inject” the dependency where it’s needed, and only where it’s
   needed.
   This way, the dependency can’t be seen nor used by anyone that doesn’t need
   it and callers are none the wiser.

And this is where the word “Injection” comes into play.

Java and Kotlin have a great mechanism for DI (they are not the only ones, but
I’ll focus on them since their approach is fairly well known by now).

It’s very simple: you use the annotation @Inject to mark values as dependencies,
and this annotation can be used in different places based on your scenario. The
limitation of this annotation in Java and Kotlin is that it cannot be directly
injected into a function (and just that function), so instead, we declare it in
the class that this function belongs to, but the effect is the same:

class PacketSender {
    @Inject
    val logger: Logger
    fun sendPacket(packet: Packet) {
        logger.log("Hello")
        // send packet
    }
}

The encapsulation aspect is neatly fixed by this approach: the developer of the
PacketSender class can add and remove dependencies with abandon without ever
impacting the signature of sendPacket(), which means that its users will never
break.

The next issue to address is how to instantiate a class like PacketSender with
the right dependencies, but I will leave that aside since this is a problem
that’s pretty well documented and solved by the various DI libraries that are
available today. Instead, I’d like to follow these observations with a short
discussion about how we could improve on this mechanism even further.


A LANGUAGE WITH NATIVE SUPPORT FOR DI

As I mentioned above, the one limitation of the Java/Kotlin approach to DI is
that the injection is still too broad: it needs to be done at the class level,
which means these dependencies are visible to all the functions of the class.
Can we do better?

Well, not with the current versions of Java and Kotlin, but let’s think about
what this could look like.

We have established that functions need two kinds of parameters:

 * “Business parameters”, values that the function needs to perform its
   function.
 * “Dependency parameters” (or we could call them “Support” or “Utility”
   parameters), values that the function can use for reasons that are not
   directly related to its function and which should be kept as private
   implementation details.

The interesting observation here is that these two kinds of parameters are
coming from different origins, and the signature of the function should reflect
that. Which led me to the following (hypothetical) syntax:

fun sendPacket(packet: Packet)(logger: Logger) {
    logger.log("Hi")
    // send packet
}

We now have two lists of parameters, with the second one being optional. Any
parameter mentioned in that second list of parameters will have to be supplied
by the DI runtime of the language and its parameters are only visible while
inside the function. Callers of such functions only have to supply the
parameters in the first list and can completely ignore the dependency parameter
list.

With this approach, we achieve the desired encapsulation and we cleanly separate
the parameters in two very distinct categories, which improves the readability
of the code without compromising type safety. Dependencies are created
“somewhere else” (to be defined, but like I said above, existing DI frameworks
have given us plenty of interesting mechanisms to build these dependencies) and
passed transparently to functions without ever impacting callers.

Guice’s “Assisted Injection” offers a mechanism that’s very similar to what I’m
proposing (here is an article with more details about it), but other than that,
I haven’t found any language trying to solve this particular problem. Please let
me know if you know of any!




WHAT KOTLIN COULD LEARN FROM RUST

Nov 9

Posted by Cedric in Uncategorized | 7 Comments

This is a follow up to my previous article, in which I explored a few aspects of
Kotlin that Rust could learn from. This time, I am going to look at some
features that I really enjoy in Rust and which I wish that Kotlin adopted.

Before we begin, I’d like to reiterate that my point is not to start a language
war between the two languages, nor am I trying to turn one language into the
other. I spent careful time analyzing which features I want to discuss and
automatically excluded features that make perfect sense for one language and
would be absurd in the other. For example, it would be silly to ask for garbage
collection in Rust (since its main proposition is a very tight control on memory
allocation) and reciprocally, it would make no sense for Kotlin to adopt a
borrow checker, since the fact that Kotlin is garbage collected is one of its
main appeals.

The features I covered in my first article and in this one are functionalities
which I think could be adopted by either language without jeopardizing their
main design philosophies, although since I don’t know the internals of either
languages, I might be off on some of these, and I welcome feedback and
corrections.

Let’s dig in.


MACROS

I have always had a love hate relationship with macros in languages, especially
non hygienic ones. At the very least, macros should be fully integrated in the
language, which requires two conditions:

 * The compiler needs to be aware of macros (unlike for example, the
   preprocessor in C and C++).
 * Macros need to have full access to a statically typed AST and be able to
   safely modify this AST.

Rust macros meet these two requirements and as a result, unlock a set of very
interesting capabilities, which I’m pretty sure we’ve only started exploring.

For example, the dbg!() macro:

let a = 2;
let b = 3;
dbg!(a + b);

Will print

[src\main.rs:158] a + b = 5

Note: not just the source file and line number but the full expression that’s
being displayed (“a + b”).

Another great example of the power of macros can be seen in the debug_plotter
crate, which allows you to plot variables:

fn main() {
    for a in 0..10 {
        let b = (a as f32 / 2.0).sin() * 10.0;
        let c = 5 - (a as i32);
        debug_plotter::plot!(a, b, c; caption = "My Plot");
    }
}

How beautiful and geeky is that?

Kotlin is not completely unarmed in this department since the mix of annotations
and annotation processors provide a set of functionality that is not very far
from what you can do in Rust with macros and attributes. The main difference is
that while Kotlin’s approach only allows for legal Kotlin code to ever be
present in a Kotlin source file, Rust allows for any arbitrary syntax to appear
as a macro argument, and it’s up to the macro to generate correct Rust that the
compiler will accept.

I have to admit that my mind is not fully made on this particular aspect.

On one hand, it’s nice to be able to write any kind of code in a Rust source
file (this is what React does with JSX), on the other hand, the potential for
abuse is high and one can rightfully fear a day when a Rust source file would
look nothing like Rust code. However, so far, my fear has never materialized and
most macros that I have encountered use custom syntaxes very parsimoniously.

Another very important aspect of macros is that Rust IDE’s understand them
(well, at least, CLion does, and potentially, all IDE’s can, and will) and they
will immediately show you errors when something is going wrong.

Macros are used in a very large array of scenarios and provide Rust with some
really neat DSL capabilities (e.g. for libraries supporting SQL, Web, graphics,
etc…).

Also, macros integrate very neatly with…


ATTRIBUTES FOR PREPROCESSING

Attributes are Rust’s version of annotations and they start either with # or #!:

#![crate_type = "lib"]
#[test]
fn test_foo() {}

Nothing groundbreaking here, but what I want to discuss is the conditional
compilation aspect.

Conditional compilation is achieved in Rust by combining attributes and macros
with cfg, which is available as both an attribute and a macro.

The macro version allows you to compile conditionally a statement or an
expression:

#[cfg(target_os = "macos")]
fn macos_only() {}

In the code above, the function macos_only() will only be compiled if the
operating system is macOS.

The macro version of cfg() allows you to add more logic to the condition:

let machine_kind = if cfg!(unix) {
    "unix"
} else { … }

At the risk of repeating myself: the above code is a macro, which means it’s
evaluated at compile time. Any part of the condition that is not meant will be
completely ignored by the compiler.

You might rightfully wonder if such a feature is necessary in Kotlin, and I
asked myself the same question.

Rust compiles to native executables, on multiple operating systems, which makes
this kind of conditional compilation pretty much a requirement if you want to
publish artifacts on multiple targets. Kotlin doesn’t have this problem since it
produces OS neutral executables that are run on the JVM.

Even though Java and Kotlin developers have learned to do without a preprocessor
since the C preprocessor left such a bad impression on pretty much everyone who
has used it, there have been situations in my career where being able to have
conditional compilation that includes or excludes source files, or even just
statements, expressions, or functions, would have come in handy.

Regardless of where you stand in this debate, I have to say I really enjoy how
two very different features in the Rust ecosystem, macros and attributes, are
able to work together to produce such a useful and versatile feature.


EXTENSION TRAITS

Extension traits allow you to make a structure conform to a trait “after the
fact”, even if you don’t own either of these. This last point bears repeating:
it doesn’t matter if the structure or the trait belong to libraries that you
didn’t write. You will still be able to make that structure conform to that
trait.

For example, if we want to implement a last_digit() function on the type u8:

trait LastDigit {
    fn last_digit(&self) -> u8;
}
impl LastDigit for u8 {
    fn last_digit(&self) -> u8 {
        self % 10
    }
}
fn main() {
    println!("Last digit for 123: {}", 123.last_digit());
    // prints “3”
}

I might be biased about this feature because unless I am mistaken, I was the
first person to suggest a similar functionality for Kotlin back in 2016 (link to
the discussion).

First of all, I find the Rust syntax elegant and minimalistic (even better than
Haskell’s and arguably, better than the one I proposed for Kotlin). Second,
being able to extend traits this way unlocks a lot of extensibility and power in
how you can model problems, but I’m not going to dive too deep into this topic
since it would take too long (look up “type classes” to get a sense of what you
can achieve).

This approach also allows Rust to mimic Kotlin’s extension functions while
providing a more general mechanism to extend not just functions but types as
well, at the expense of a slightly more verbose syntax.

In a nutshell, you have the following matrix:

KotlinRustExtension functionfun Type.function() {...}Extension traitExtension
traitN/AExtension trait


CARGO

This probably comes off as a surprise since with Gradle, Kotlin has a very
strong build and package manager. The two tools certainly have the same
functional surface area, allowing to build complex projects while also managing
library downloading and dependency resolution.

The reason why I think cargo is a superior alternative to Gradle is because of
its clean separation between the declarative syntax and its imperative side. In
a nutshell, standard, common build directives are specified in the declarative
cargo.toml file while ad hoc, more programmatic build steps are written directly
in Rust in a file called build.rs, using Rust code calling into a fairly
lightweight build API.

In contrast, Gradle is a mess. First because it started being specified in
Groovy and it now supports Kotlin as the build language (and this transition is
still ongoing, years after it started), but also because the documentation of
both of those is still incredibly bad

By “bad”, I don’t mean “lacking”: there is a lot of documentation, it’s just…
bad, overwhelming, most of it outdated, or deprecated, etc…, requiring hundreds
of lines of copy/paste from StackOverflow as soon as you need something out of
the beaten path. The plug-in system is very loosely defined and basically lets
all plug-ins access whatever they feel like inside Gradle’s internal structures.

Obviously, I am pretty opinionated on this topic since I created a build tool
inspired by Gradle but using more modern approaches to syntax and plug-in
resolution (it’s called Kobalt), but independently of this, I think cargo
manages to strike a very fine balance between a flexible build+dependency
manager tool that covers all the default configuration adequately without being
overwhelmingly complex as soon as your project grows.


U8, U16, …

In Rust, number types are pretty straightforward: u8 is an 8 bit unsigned
integer, i16 is a 16 bit signed integer, f32 is a 32 bit float, etc…

This is such a breath of fresh air to me. Until I started using these types, I
had never completely identified how uncomfortable I had always been with the way
C, C++, Java, etc… define these types. Whenever I needed a number, I would use
int or Long as default. In C, I sometimes went as far as long long without
really understanding the implications.

Rust forces me to pay very close attention to all these types and then, the
compiler will relentlessly keep me honest whenever I try to perform casts that
can lead to bugs. I really think that all modern languages should follow this
convention.


COMPILER ERROR MESSAGES

Not to say that Kotlin’s error messages are bad, but Rust certainly set a new
standard here, in multiple dimensions.

In a nutshell, here is what you can expect from the Rust compiler:

 * ASCII graphics with arrows, colors, clear delineations of problematic
   sections.
 * Plain English and detailed error messages.
 * Suggestions on how you could fix the problem.
 * Links to relevant documentation where you can find out more about the
   problem.

I certainly hope that future languages will take inspiration.


PORTABILITY

About twenty-five years ago, when Java came out, the JVM made a promise: “Write
once, run anywhere” (“WORA”).

While this promise stood on shaky grounds in the early years, there is no
denying that WORA is a reality today, and has been for a couple of decades. Not
only can JVM code be written once and run everywhere, such code can also be
written anywhere, which represents an important productivity boost for
developers. You can write your code on any of Windows, macOS, Linux, and deploy
on any of Windows, macOS, and Linux.

Surprisingly, Rust is also capable of such versatility, even though it produces
native executables. Regardless of the operating system you are writing your code
on, producing executables for a multitude is trivial, with the added benefit
that these executables are native and, thanks to the incredible technical
achievement that the LLVM is, very performant too.

Before Rust, I had resigned myself to the fact that if I wanted to run on
multiple operating systems, I had to pay the price of running on a virtual
machine, but Rust is now showing that you can have your cake and eat it too.

Kotlin (and the JVM in general) is beginning to learn this lesson too, with
initiatives such as GraalVM, but producing executables for JVM code is still
fraught with restrictions and limitations.


WRAPPING UP

I have a lot more to say about all of this.

And by “all of this”, I mean “Rust and Kotlin”.

They are both such interesting languages. I like them both, but for different
reasons. I hope I was able to convey some of my fondness in these two posts.
Even though these articles might appear critical, they are really love letters.
I am a very demanding developer, someone who’s been writing code for forty years
and who plans to keep doing so for as long as his mental abilities allow him. I
feel unreasonably passionate about programming languages, and I hope that my
passion shone through these two articles.

TestNG is a project I started around 2004 with the only intent to mix things up.
I wanted to show the Java world that we could do better than JUnit. I had no
intentions for anyone to like and adopt TestNG: it was a project lab. An
experiment. All I wanted to do was to show that we could do better. I was
genuinely hoping that the JUnit team (or whatever was left of it) would take a
look at TestNG and think “Wow, never thought of that! We can incorporate these
ideas in JUnit and make it even better!”.

This is my goal with these couple of posts. I would be ecstatic if these two
very, very different worlds (the Rust and Kotlin communities) would pause for a
second from their breakneck development pace, take a quick look at each other,
even though they really had no interest in doing so, and realize “well… that’s
interesting… I wonder if we could do this?”.

Discussions on reddit:

 * /r/rust
 * /r/Kotlin




WHAT RUST COULD LEARN FROM KOTLIN

Oct 20

Posted by Cedric in Uncategorized | Comments off

[Update: Part Two is up]

When I started studying Rust, I didn’t expect to like it.

Ever since I heard about it, many years ago, I’ve always had a tremendous amount
of respect for the design philosophy and the goal that Rust was trying to
achieve, all the while thinking this is not the language for me. When I switched
from ten years of gruesome C++ to Java back in 1996, I realized that I was no
longer interested in low level languages, especially languages that force me to
care about memory. My brain is wired a certain way, and that way makes low level
considerations, especially memory management, the kind of problem that I derive
little pleasure working on.

Ironically, despite my heavy focus on high level languages, I still maintain a
healthy fascination for very low level problems, such as emulators (I wrote
three so far: CHIP-8, 8080 Space Invaders, and Apple ][, all of which required
me to become completely fluent in various assembly languages), but for some
reason, languages that force me to care about memory management have always left
me in a state of absolute dismissal. I just don’t want to deal with memory, ok?

But… I felt bad about it. Not just because the little Rust I knew piqued my
curiosity, but also because I thought it would be a learning exercise to embrace
its design goal and face memory management heads on. So I eventually decided to
learn Rust, more out of curiosity and to expand my horizons than to actually use
it. And what I found surprised me.

I ended up liking writing code in it quite a bit. For a so-called “system
language”, its design was a breath of fresh air and proof that its creators had
not only a vision but also a healthy knowledge of programming language theory,
which was refreshing after seeing some… other languages that have appeared in
the past fifteen years. I will resist giving names, but I’m sure you know what
I’m talking about.

This article is not intended to start a language war. I love both Kotlin and
Rust. They are both great languages, both with some flaws, and I am extremely
happy to be able to claim a decent understanding of both of them, and to feel
equally comfortable to start new projects in either, whichever is the best
language for the job.

But these languages have followed different paths and ended up making different
compromises, which makes their simultaneous study and comparison extremely
interesting to me.

It took me a while to select which features I wanted to include in this list but
eventually, I narrowed my selection criterion to a very simple one: it has to be
a feature that will not get in the way of Rust’s main value propositions (close
to optimal memory management, zero cost abstractions). That’s it.

I think all the features that I describe in this article are of the cosmetic,
but crucial, variety. They will enhance the readability and writability of the
language without compromising Rust’s relentless pursuit of zero cost memory
management. However, since I’m obviously not familiar with the internals of the
Rust compiler, some of these might indeed compromise Rust’s laser focus on
optimal memory management, in which case I’d love to be corrected.

Enough preamble, let’s dig in. To give you an idea of what lies ahead, here are
the Kotlin features that I’ll be discussing below:

 * Short constructor syntax
 * Overloading
 * Default parameters
 * Named parameters
 * Enums
 * Properties


CONSTRUCTORS AND DEFAULT PARAMETERS

Let’s say we want to create a Window with coordinates and a boolean visibility
attribute, which defaults to false. Here is what it looks like in Rust:

struct Window {  x: u16,
  y: u16,
  visible: bool,
}
impl Window {
  fn new_with_visibility(x: u16, y: u16, visible: bool) -> Self {
    Window {
      x, y, visible
    }
  }
  fn new(x: u16, y: u16) -> Self {
    Window::new_with_visibility(x, y, false)
  }
}



And now in Kotlin:

class Window(x: Int, y: Int, visible: Boolean = false)

That’s a huge difference. Not just in line count, but in cognitive overload.
There is a lot to parse in Rust before you conceptually understand what this
class is and does, whereas reading one line in Kotlin immediately gives you this
information.

Admittedly, this is a pathological case for Rust since this simple example
contains all the convenient syntactic sugaring that it’s lacking, namely:

 * A compact constructor syntax
 * Overloading
 * Default parameters

Even Java scores better than Rust here, since it supports at least overloading
(but fails on the other two features).

Even to this day, I whine whenever I have to write all this boilerplate in Rust,
because you write this kind of code all the time. After a while, it becomes a
second nature to parse it, a bit like when you see getters and setters in Java,
but it’s still unnecessary cognitive overload, which Kotlin has solved
elegantly.

The lack of overloading is the most baffling to me. First, this forces me to
come up with unique names, but mostly because it’s a compiler feature that’s
pretty trivial to implement in general, which is why most (all?) mainstream
languages created these past twenty years support it. Why force the developer to
come up with new names while the compiler can do it automatically, and by doing
so, reduce the cognitive load on developers and make the code easier to read?

The common counter argument to overloading is about interoperability: once the
compiler generates mangled function names, it can become tricky to call these
functions from other processes or from other languages. But this objection is
trivially resolved by allowing the developer to disable name mangling for
specific cases (which is exactly what Kotlin does, and its interoperability with
Java is outstanding). Since Rust already relies heavily on attributes, a
#[no_mangle] attribute would fit right in (and guess what, it’s already been
discussed).


NAMED PARAMETERS

This is a feature that I consider more “nice to have” than really essential, but
optionally named parameters can contribute to reducing a lot of boilerplate as
well. They are especially effective at reducing the need for builder patterns,
since you can now limit the use of this design pattern to parameter validation,
instead of needing it as soon as you need to build complex structures.

Here again, Kotlin hits a sweet spot by allowing to name parameters but not
requiring you to use these names all the time (a mistake that both Smalltalk and
Objective C made). Therefore, you get the best of both worlds: most of the time,
invoking a function is intuitive enough without naming the parameters, but now
and then, they come in very handy to disambiguate complex signatures.

For example, imagine we add a boolean to the Window structure above to denote
whether our window is black and white:

class Window(x: Int, y: Int, visible: Boolean = false, blackAndWhite: Boolean = false)

Without named parameters, calls to the constructor can be ambiguous to a reader:

val w = Window(0, 0, false, true) // mmmh, which boolean means what?

Kotlin lets you mix unnamed and named parameters to disambiguate the call:

val w = Window(0, 0, visible = false, blackAndWhite = true)

Note that in this code, x and y are not explicitly named (because their meaning
is implied to be obvious), but the boolean parameters are.

As an added bonus, named parameters can be used in any order, which reduces the
cognitive load on the developer since you no longer need to remember in which
order these parameters are defined. Note also that this feature combines
harmoniously with default parameters:

// skip 'visible', use its default value
val w = Window(0, 0, blackAndWhite = true)

In the absence of this feature, you will have to define an additional
constructor in your Rust structure, one for each combination of parameters that
you want to support. If you are keeping count, you now need four constructors:

 * x, y
 * x, y, visible
 * x, y, black_and_white
 * x, y, visible, black_and_white

You can see how this quickly leads to a combinatorial explosion of functions for
something which should realistically only take one line of code, as Kotlin
demonstrates.


ENUMS

For all the (mostly justified) criticism that Java receives because of its
design, there are a few features that it supports that are arguably best in
class, and in my opinion, Java enums (and by extension, Kotlin’s as well) are
the best designed enums that I have ever used.

And the reason is simple: Java/Kotlin enums are very close to being regular
classes, with all the advantages that these classes bring, with Kotlin’s enums
being a superset of Java’s, so even more powerful and flexible.

Rust’s enums are almost as good, but they omit one critical component that makes
them not as practical as Java’s: they don’t support values in their constructor.

I’ll give a quick example. One of my recent projects was to write an Apple][
emulator, which includes a 6502 processor emulator. Processor emulation is a
pretty easy problem to solve: you define opcodes with their hexadecimal value,
string representation, and size, and you implement a giant switch to match the
bytes that you read from the file against these opcodes.

In Kotlin, you could define these opcodes as enums as follows:

enum class Opcode(val opcode: Int, val opName: String, val size: Int) {
  BRK(0x00, "BRK", 1),
  JSR(0x20, "JSR", 3)
  //...
}

While Rust’s enums are pretty powerful overall (especially when coupled with
Rust’s destructuring match syntax), they only allow you to define signatures for
each of your enum instances (which Kotlin supports too) but you can’t define
parameters at your enum level, so the code above is just impossible to replicate
in a Rust enum.

My solution to this specific problem was to first define all the opcodes as
constants and put them in a vector as tuples:

pub const BRK: u8 = 0x00;
pub const JSR: u8 = 0x20;
// ...
// opcode hex, opcode name, instruction size
let ops: Vec<(u8, &str, usize)> = vec![
  (BRK, "BRK", 1),
  (JSR, "JSR", 3),
  // ...
];


And then enumerate this vector to create instances of an Opcode structure, and
put these in a HashMap, indexed by their opcode value:

struct Opcode {
  opcode: u8,
  name: &'static str,
  size: usize,
}
let mut result: HashMap<u8, Opcode> = HashMap::new();
for op in ops {
  result.insert(op.0, Opcode::new(op.0, op.1, op.2));
}

I’m sure there are various ways to reach the same result, but they all end up
with a significant amount of boilerplate, and since it’s not really possible to
use Rust’s enums here, we lose the benefits that they have to offer. Allowing
Rust enums to be instantiated with constant values would significantly decrease
the amount of boilerplate and make it a lot more readable as a result.


PROPERTIES

This was another unpleasant step back, and I still find it routinely painful in
Rust to have to write getters and setters for all the fields that I want to
expose from a structure. We learned this lesson the hard way with Java, and even
today in 2021, getters and setters are still alive and well in this language.
Thankfully, Kotlin does it right (it’s not the only one, C#, Scala, Groovy, …
get it right too). We know that having properties and universal access is of
great value, it’s disappointing that we don’t have this feature in Rust.

As a consequence, whenever you release code that is going to be used by third
parties, you need to be very careful if you make a field public, because once
clients start referencing that field directly (read or write), you no longer
have the luxury of ever putting it behind a getter or a setter, or you will
break your callers. And as a result, you are probably going to err on the side
of caution and manually write getters and setters.

We know better today, and I hope Rust will adopt properties at some point in the
future.


CONCLUSION

So this is my list. None of these missing features have been an obstacle to
Rust’s meteoritic rise, so they are obviously not critical, but I think they
would contribute to making Rust a lot more comfortable and more pleasant to use
than it is today.It should come as no surprise that Kotlin could also learn a
few things from Rust, so I’m planning on following up with a reverse post which
will analyze some features from Rust that I wish Kotlin had.

Special thanks to Adam Gordon Bell for reviewing this post.

Update:

Discussions on reddit:

 * /r/rust
 * /r/programming



Tags: Java, Kotlin, Rust


REFACTORING A DYNAMICALLY TYPED LANGUAGE: DO IT SAFELY OR AUTOMATICALLY, BUT NOT
BOTH

Jun 20

Posted by Cedric in Uncategorized | 5 Comments

I was recently having a discussion about refactoring dynamically typed languages
and I was struck by the amount of misconceptions that a lot of developers still
have on this topic.

I expressed my point in this article from fifteen years ago(!), not much has
changed, but the idea that it it impossible to safely and automatically refactor
a language that doesn’t have type annotations is still something that is not
widely accepted, so I thought I would revisit my point and modernize the code a
bit.

First of all, my claim:

> In languages that do not have type annotations (e.g. Python, Ruby, Javascript,
> Smalltalk), it is impossible to perform automatic refactorings that are safe,
> i.e., that are guaranteed to not break the code. Such refactorings require the
> supervision of the developer to make sure that the new code still runs.

First of all, I decided to adapt the snippet of code I used in my previous
article and write it in Python. Here is a small example I came up with:

class A:
    def f(self):
        print("A.f")
class B:
    def f(self):
        print("B.f")
if __name__ == '__main__':
    if random() > 0.5:
        x = A()
    else:
        x = B()
    x.f()

Pretty straightforward: this code will call the function f() on either an
instance of class A or B.

What happens when you ask your IDE to rename f() to f2()? Well, this is
undecidable. You might think it’s obvious that you need to rename both A.f() and
B.f(), but that’s just because this snippet is trivial. In a code base
containing hundreds of thousands of lines, it’s plain impossible for any IDE to
decide what functions to rename with the guarantee of not breaking the code.

This time, I decided to go one step further and to actually prove this point,
since so many people are still refusing to accept it. So I launched PyCharm,
typed this code, put the cursor on the line x.f() and asked the IDE to rename
f() to f2(). And here is what happened:

class A:
    def f2(self):
        print("A.f")
class B:
    def f(self):
        print("B.f")
if __name__ == '__main__':
    if random() > 0.5:
        x = A()
    else
        x = B()
    x.f2()

PyCharm renamed the first f() but not the second one! I’m not quite sure what
the logic is here, but well, the point is that this code is now broken, and you
will only find out at runtime.

This observation has dire consequences on the adequacy of dynamically typed
languages for large code bases. Because you can no longer safely refactor such
code bases, developers will be a lot more hesitant about performing these
refactorings because they can never be sure how exhaustive the tests are, and in
doubt, they will decide not to refactor and let the code rot.

Update: Discussion on reddit.




MALWARE ON MY ANDROID PHONE!

Jan 10

Posted by Cedric in Uncategorized | Comments off

I have a confession to make that I’m not very proud of: recently, I unwittingly
installed malware on my Android phone. As one of the early members of the
Android team and someone who’s been using Android for about thirteen years, this
was a pretty humbling and irritating event. This is what happened.

I remember how it started: I unlocked my phone and two accidental clicks led me
to agree to a dialog that my brain immediately registered as suspicious. But I
had other things on my mind at the time so I paid it no mind and moved on.

The next day, I picked up my phone and when I launched Chrome, I immediately
noticed it was displaying a spammy URL. What’s worse: there were over ten tabs
displaying similar URL’s which I was certainly not visiting before going to bed.
This is when I realized what had happened.

DEFCON 5

Lacking the time to do an investigation at the time, I went for an easy and
temporary solution: changed my default browser from Chrome to another one and
moved on. A few hours later, I found a similar set of URL’s displayed by my new
default browser, which now definitely pointed the finger toward a rogue app.

DEFCON 4

I went through all my apps and uninstalled and disabled a bunch of them. I also
downloaded MalwareBytes and ran it, to no effect. Google Play Protect also did
not notice anything suspicious on my phone. I must have missed the offending app
because the URL’s kept showing.

DEFCON 3

Time to go medieval. I hooked up my phone and launched adb. I started by
inspecting the list of recurring tasks, but the output was so voluminous that
finding anything useful was a dim prospect. I also inspected all the services
and their associated applications, but then again, it was pretty much impossible
to find anything suspicious even with some well targeted `greps`.

DEFCON 2

I resigned myself to the brute force approach. I launched Android Studio,
filtered the logcat output on “http.?://“, moved the window on the side and
resumed my activities while keeping an eye on which URL’s my phone visits while
I’m not using it.

It only took about an hour until one of the fishy URL’s showed up:

2021-01-09 11:01:14.651 3655-4415/? I/ActivityTaskManager: START u0 
{act=android.intent.action.VIEW dat=https://vbg.dorputolano.com/...
 flg=0x10000000 cmp=org.adblockplus.browser.beta/com.google.android.apps.chrome.IntentDispatcher} from uid 10237


Ha HA! I got you now. I have a uid, which I grepped through the output, and I
finally identified my target:

2021-01-09 11:01:13.810 3655-3655/? I/EdgeLightingManager: showForNotification :
isInteractive=false, isHeadUp=true, color=0,
sbn = StatusBarNotification(pkg=com.qrcodescanner.barcodescanner user=UserHandle{0} id=1836 tag=null 
key=0|com.qrcodescanner.barcodescanner|1836|null|10237: Notification(channel=sfsdfsdfsd pri=2 contentView=null
vibrate=null sound=null defaults=0x0 flags=0x90 color=0x00000000 vis=PRIVATE semFlags=0x0 semPriority=0 semMissedCount=0))


So the package name is “com.qrcodescanner.barcodescanner“. It looks like the
rogue app is disguising itself as a QR barcode scanner. I took another look at
the list of my apps and sure enough, I quickly located the offending
application. I uninstalled it, and a few hours later, I was happy to observe
that the URL’s stopped popping up.

BACK TO DEFCON 5

I went back to the Play Store and tried to find the application that I
uninstalled but couldn’t find it, which indicates that Google probably removed
it from its store some time ago. The malware I activated most likely side loaded
it after I unwittingly approved its installation.

Some forensic research revealed an article from 2018 discussing such a malware
QR Code Reader application, but the package and the mode of operation don’t
quite match what I found. I am probably dealing with a copycat.

Looking back, I feel pretty disappointed that I had to go through all these
steps to get rid of a simple scam application. What would a regular user do?

CONCLUSIONS AND SUGGESTIONS

 * Listing the apps installed on my phone should give me the option to sort them
   by “Latest installed”. I am pretty sure that if I had had this option and I
   had seen a QR Code Scanner installed just a few days ago, it would have
   immediately grabbed my attention. As it is, the way Android lists the
   installed apps is pretty useless for this purpose.
   
 * MalwareBytes was completely useless and I immediately uninstalled it when I
   realized this fact. The problem is that it was probably just looking for
   malware code signatures inside the packages instead of just looking at which
   apps I had installed.
   
 * Google Play Protect was also completely unhelpful, which was a big
   disappointment. First because Google certainly knows which applications they
   removed from their store for malware reasons, but even so, I would expect
   Google Play Protect to at least flag any app it finds on my phone that is not
   on their store. Such an app is not necessarily malware, but it should
   certainly be flagged.
   
 * Google Play Protect could also do some behavior profiling to analyze what
   apps are doing in the background. A service launching recurring VIEW intents
   on web sites in the background should have raised a flag to the system.


ZOOM BACKGROUND #23

Sep 20

Posted by Cedric in Uncategorized | Comments off




ZOOM BACKGROUND #22

Sep 14

Posted by Cedric in Uncategorized | Comments off


A flour windmill on the island of Leros, Greece.


ZOOM BACKGROUND #21

Sep 10

Posted by Cedric in Uncategorized | Comments off


This is what the sky looks like in the Bay Area this week. The orange glow is
due to high altitude smoke from the fires that are ravaging the area. The air
quality is actually not too bad because the smoke is much higher than usual.


ZOOM BACKGROUND #20

Aug 31

Posted by Cedric in Uncategorized | Comments off


Screen shot Flight Simulator 2020. The game receives real time weather data and
this is a model of hurricane Laura as it was unfolding.

« Older Entries



 * TOP ENTRIES
   
    * I am the reason for Hungarian notation in Android
    * Dynamic language, refactoring: pick one
    * Announcing Kobalt 1.0
    * The Kobalt diaries: Templates
    * Hardcore multicore with TestNG
    * More on parallel topological sorting
    * The return of statically typed languages
    * Structural typing vs/ duck typing
    * Agile people still don’t get it
    * Various ways to get randomness wrong
    * Coding challenge
    * Coding challenge wrap up

 * 

 * RECENT POSTS
   
    * Viewing the world with mathematics
    * It’s called Dependency “Injection” for a reason
    * What Kotlin could learn from Rust
    * What Rust could learn from Kotlin
    * Refactoring a dynamically typed language: do it safely or automatically,
      but not both

 * RECENT COMMENTS
   
    * Laurent Simon on It’s called Dependency “Injection” for a reason
    * Michael Bayne on It’s called Dependency “Injection” for a reason
    * pj on What Kotlin could learn from Rust
    * John on What Kotlin could learn from Rust
    * Cedric on What Kotlin could learn from Rust

 * ARCHIVES
   
    * February 2023
    * May 2022
    * November 2021
    * October 2021
    * June 2021
    * January 2021
    * September 2020
    * August 2020
    * July 2020
    * June 2020
    * July 2017
    * March 2017
    * February 2017
    * August 2016
    * June 2016
    * May 2016
    * April 2016
    * February 2016
    * January 2016
    * November 2015
    * October 2015
    * August 2015
    * July 2015
    * June 2015
    * May 2015
    * April 2015
    * March 2015
    * December 2014
    * July 2014
    * June 2014
    * May 2014
    * January 2014
    * August 2013
    * July 2013
    * March 2013
    * February 2013
    * January 2013
    * December 2012
    * September 2012
    * August 2012
    * July 2012
    * June 2012
    * April 2012
    * March 2012
    * February 2012
    * January 2012
    * December 2011
    * November 2011
    * October 2011
    * September 2011
    * August 2011
    * July 2011
    * June 2011
    * May 2011
    * April 2011
    * March 2011
    * February 2011
    * January 2011
    * December 2010
    * November 2010
    * October 2010
    * September 2010
    * August 2010
    * July 2010
    * June 2010
    * May 2010
    * April 2010
    * March 2010
    * February 2010
    * January 2010
    * December 2009
    * November 2009
    * October 2009
    * September 2009
    * August 2009
    * July 2009
    * June 2009
    * April 2009
    * March 2009
    * February 2009
    * November 2008
    * October 2008
    * September 2008
    * August 2008
    * July 2008
    * June 2008
    * May 2008
    * April 2008
    * March 2008
    * February 2008
    * January 2008
    * November 2007
    * October 2007
    * September 2007
    * August 2007
    * July 2007
    * June 2007
    * May 2007
    * April 2007
    * March 2007
    * February 2007
    * January 2007
    * December 2006
    * November 2006
    * October 2006
    * September 2006
    * August 2006
    * July 2006
    * June 2006
    * May 2006
    * April 2006
    * March 2006
    * February 2006
    * January 2006
    * December 2005
    * November 2005
    * October 2005
    * September 2005
    * August 2005
    * July 2005
    * June 2005
    * May 2005
    * April 2005
    * March 2005
    * February 2005
    * January 2005
    * December 2004
    * November 2004
    * October 2004
    * September 2004
    * August 2004
    * July 2004
    * June 2004
    * May 2004
    * April 2004
    * March 2004
    * February 2004
    * January 2004
    * December 2003
    * November 2003
    * October 2003
    * September 2003
    * August 2003

 * CATEGORIES
   
    * Android
    * Apple
    * Coding challenge
    * General
    * Java
    * Kobalt
    * Kotlin
    * Mac
    * neural_networks
    * Programming languages
    * Ruby
    * Rx
    * Scala
    * TestNG
    * Uncategorized
    * Video Games
    * Zoom

 * META
   
    * Log in
    * Entries feed
    * Comments feed
    * WordPress.org



 * ARCHIVES
   
    * February 2023
    * May 2022
    * November 2021
    * October 2021
    * June 2021
    * January 2021
    * September 2020
    * August 2020
    * July 2020
    * June 2020
    * July 2017
    * March 2017
    * February 2017
    * August 2016
    * June 2016
    * May 2016
    * April 2016
    * February 2016
    * January 2016
    * November 2015
    * October 2015
    * August 2015
    * July 2015
    * June 2015
    * May 2015
    * April 2015
    * March 2015
    * December 2014
    * July 2014
    * June 2014
    * May 2014
    * January 2014
    * August 2013
    * July 2013
    * March 2013
    * February 2013
    * January 2013
    * December 2012
    * September 2012
    * August 2012
    * July 2012
    * June 2012
    * April 2012
    * March 2012
    * February 2012
    * January 2012
    * December 2011
    * November 2011
    * October 2011
    * September 2011
    * August 2011
    * July 2011
    * June 2011
    * May 2011
    * April 2011
    * March 2011
    * February 2011
    * January 2011
    * December 2010
    * November 2010
    * October 2010
    * September 2010
    * August 2010
    * July 2010
    * June 2010
    * May 2010
    * April 2010
    * March 2010
    * February 2010
    * January 2010
    * December 2009
    * November 2009
    * October 2009
    * September 2009
    * August 2009
    * July 2009
    * June 2009
    * April 2009
    * March 2009
    * February 2009
    * November 2008
    * October 2008
    * September 2008
    * August 2008
    * July 2008
    * June 2008
    * May 2008
    * April 2008
    * March 2008
    * February 2008
    * January 2008
    * November 2007
    * October 2007
    * September 2007
    * August 2007
    * July 2007
    * June 2007
    * May 2007
    * April 2007
    * March 2007
    * February 2007
    * January 2007
    * December 2006
    * November 2006
    * October 2006
    * September 2006
    * August 2006
    * July 2006
    * June 2006
    * May 2006
    * April 2006
    * March 2006
    * February 2006
    * January 2006
    * December 2005
    * November 2005
    * October 2005
    * September 2005
    * August 2005
    * July 2005
    * June 2005
    * May 2005
    * April 2005
    * March 2005
    * February 2005
    * January 2005
    * December 2004
    * November 2004
    * October 2004
    * September 2004
    * August 2004
    * July 2004
    * June 2004
    * May 2004
    * April 2004
    * March 2004
    * February 2004
    * January 2004
    * December 2003
    * November 2003
    * October 2003
    * September 2003
    * August 2003

 * CATEGORIES
   
    * Android
    * Apple
    * Coding challenge
    * General
    * Java
    * Kobalt
    * Kotlin
    * Mac
    * neural_networks
    * Programming languages
    * Ruby
    * Rx
    * Scala
    * TestNG
    * Uncategorized
    * Video Games
    * Zoom

 * META
   
    * Log in
    * Entries feed
    * Comments feed
    * WordPress.org



Arclite theme by digitalnature | powered by WordPress

Entries (RSS) and Comments (RSS) TOP