Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

2024-09-16

Jevons Is a Paradox, Not a Rule

Jevons Paradox says that as technology becomes more efficient, overall resource consumption can increase. This was seen during the Industrial Revolution when more efficient coal engines led to higher coal usage. However, this paradox is not universal, and efficiency can also lead to reduced resource consumption.

In the context of AI coding tools (e.g. GitHub Copilot), there's a belief that increased efficiency will lead to more coding jobs by lowering development costs. While this may happen, history shows that technological advancements can also displace workers.

Counter Examples

The invention of programming compilers made coding more efficient but reduced demand for assembly language programmers, who were once critical to assembly-based software development. While many of those programmers probably found other coding jobs in higher-level languages, Jevons simply doesn't guarantee it.

Similar patterns have occurred in other industries more starkly. The mechanization of agriculture reduced the need for farm labor.  See this graph:

https://ourworldindata.org/grapher/number-of-people-employed-in-agriculture

Then there's the replacement of draft horses, where ICE vehicles meant horses were no longer needed and millions of draft horses were slaughtered or displaced, and their population dwindled.  See this graph:

https://www.researchgate.net/publication/338480301/figure/fig1/AS:845430833283085@1578577826802/Evolution-of-the-horse-population-in-France-from-1800-to-2010-translated-from-French.ppm

In recent years, coal consumption has fallen despite energy efficiency gains due to the shift to other energy sources (e.g. renewables, gas).

The rebound effect, which drives Jevons Paradox, doesn’t always occur at full strength. For example, energy-efficient LED lighting and fuel-efficient cars have reduced overall energy and fuel consumption, despite potentially increasing usage. Similarly, AI tools may lead to fewer coding jobs, even if more code is produced.

Ultimately, while AI could increase software development demand, it may also reduce the need for certain types of programmers. History shows that efficiency gains don’t always lead to more jobs. Jevons didn't guarantee draft horses more jobs, after all.

This was written in collaboration with an AI — another example where more words will be written as efficiency per word increases but the number of writing jobs may well decrease (as it apparently already has: https://www.bbc.com/news/business-65906521 ).

2022-05-09

Trampolines - fun way to make recursion not stack overflow

(We'll use JavaScript for today, running in the Firefox developer console.)

No doubt when you learned recursion, you learned that each recursive function call uses stack space to do its work.  There's only so much stack space.  So unless your programming language has a special feature (called tail call elimination), a recursive function can eventually exhaust all stack space, leading to the famous stack overflow error (not the web site).

For example, let's write a loop to sum up numbers from 0 up to some N like this:

let sum = 0;
for (let i = 0; i < 10000; ++i) sum += i;
console.log(sum); // prints: 49995000

Now a recursive version might look like this:

const loop = function(i, sum){
  if (i < 10000){
    sum += i;
    return loop(i + 1, sum);
  } else {
    return sum;
  }
};
console.log(loop(0, 0)); // prints: 49995000

As you can see, a loop is just a recursive function call.

The above is a nice, simple demo of converting a loop to recursion.  But if instead of summing up to 10,000, we sum up to 100,000, then the loop prints 4999950000.  But the recursive function prints:

Uncaught InternalError: too much recursion

With a link to: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Too_much_recursion

Trampolines

No surprise, like the title says, trampolines can fix this!

The key is to not allow the loop function to run loop(i + 1, sum), because that'd consume stack space!  Instead, the loop function will return a function called a thunk.  That thunk function, when run, will run and return loop(i + 1, sum).  This also means a thunk can return a thunk!

The function that runs loop, and any thunk functions, is called a trampoline.  That's because the thunk function the trampoline runs may return a thunk, and if so, that thunk will get run too.  The thunks keep bouncing off of the trampoline function.  Until one day a thunk returns a value instead of a thunk!  Then the trampoline's job is done, and that value is returned.

Because the thunk itself only ever uses a single "frame" of space on the stack, rather than recursively using more and more stack space with recursive function calls, and so the stack overflow error is avoided.

Here's how the code will look:

const loop = function(i, sum){
  if (i < 100000){
    sum += i;
    let thunk = ()=>{return loop(i + 1, sum)};
    return thunk; // rather than returning loop(i + 1, sum)
  } else {
    return sum;
  }
};
 

const trampoline = function(){
  let tv = loop(0, 0);
  while (typeof tv === 'function'){
    tv = tv(); // returns either a thunk function or a value
  }
  return tv;
};
 

console.log(trampoline()); // prints: 4999950000

Trampolines running thunk-returning thunk functions is a generic technique that's applicable in other languages and situations too!


Tail Call Elimination: no need for trampolines

If your programming language has full support for an optimization called Tail Call Elimination, the above trampoline technique is completely unnecessary.

It turns out JavaScript at one point had this optimization planned.  It was to be an "invisible" opportunistic tail call optimization (TCO).  Meaning that if you correctly wrote a proper recursive function call in tail position, then TCO would kick in, and it wouldn't consume stack space (thus no stack overflow).

TCO is currently only available on Apple's Safari browser and on iOS [1].

Google's V8 team apparently came to the conclusion that TCO makes the wrong tradeoff.  Because it's an opportunistic optimization, it's very easy for a programmer to write code they think would get TCO, but actually won't, and it can be very difficult to discover the error during testing.  They advocated for an explicit syntactic way to designate a recursive function call as requiring tail call elimination.  But... well, it all fell by the wayside and was never picked back up [2].

Interestingly, the Clojure programming language's creator, Rich Hickey, basically made the same argument.  In Clojure, recursive function calls requiring TCO must be written explicitly with a recur syntax.  In that case, no trampoline is needed, and the recursive function call won't overflow the stack.

 

[1] https://kangax.github.io/compat-table/es6/

[2] https://stackoverflow.com/a/42788286

2021-04-28

Programming Language Notes 2021 - multiplatform, GUIs

These are incomplete notes and thoughts on programming languages through lens of multiplatform support and coding GUI apps for platforms like Android, iOS, Mac, Windows, Linux, and web (front and back ends).

JavaScript

Lack type safety.

Java, Go, Python, Ruby, C++, C, Elixir

Not great for frontend web dev.

D

Not great for Android or iOS.   Can build web apps via compiling to WASM (pretty sure it's experimental), but lack mature frameworks for frontend web dev.  Not very popularly used, unfortunately.

TypeScript

It's JavaScript but with a brilliant aftermarket type system retrofit. If you must code JS, then TS is a fantastic upgrade.

For backend, there's faster languages (Java, Go).  For device native apps, other languages are maybe better suited (Swift, Kotlin, etc).  Great choice for web frontend.

For frontend web dev, used with React is popular.  There's React Native to build device native apps for Macs, Windows, Linux, Android, and iOS that uses platform native UI widgets (some haven't reached 1.0 yet though, if you're looking for maturity).  You'd still have to build 5 specialized UIs though (6 including web), and there are faster device native languages.

Kotlin

Compiles to JVM, JS, and native.  Kotlin Multiplatform Mobile (alpha) is great for write-once application logic for iOS / Android native apps, but the UI code must be specialized for each platform (could still be written in Kotlin though).

e.g. Use with Google Android's Jetpack Compose (beta) and Apple's Swift UI for native Android and iOS UI.

e.g. Use with Jetbrains' Compose for Desktop to build apps for Windows, Macs, and Linux --- but this  runs on JVM and renders using Skia, so it doesn't use platform native UI widgets (it draws it's own like a game does).  And it's in alpha.

Some say Jetpack Compose is Google Android team's answer to Google Ads team's Dart/Flutter.

Kotlin/JS means you can use with React for frontend web dev too.  Not sure of its maturity.  Kotlin is great for backend using Spring or Ktor.

PHP

Not great for device native apps

C#

Windows centric.  Blazor lets you do frontend web dev by compiling to WASM but it adds C#'s runtime to your web app to run in WASM as well (read: bigger, slower app).

Rust

Lower level, like C or C++.  Can build web apps via compiling to WASM, but without bringing a runtime along for the ride (check out Yew or Seed).  Can build backend stuff (check out Actix-web or Rocket), but frameworks aren't mature the way Django or RoR are.

Coding device native GUI apps is... not there yet.

Rust is getting a lot of traction for systems programming though (unlike D, unfortunately).

Dart

Basically exists for Flutter.  Flutter lets you build apps for Windows, Macs, Linux, iOS, Android, and the web.  On the web, it draws into a canvas.  On devices, it renders using Skia.  So it doesn't use platform native UI widgets anywhere, and draws it's own like a game does.  On the web, it's UI performance is a little janky.

Dart compiles to JS or runs on Dart VM.  Unlike the Kotlin stuff above, Flutter is production ready and being used by Google, notably by their Ads team (apparently some of the Kotlin stuff above are the Android team's answer to Flutter).

It's from Google, so who knows if they'll cancel it in 5 years time.

Other thoughts

Rendering to Skia like Compose for Desktop and Flutter is not great for accessibility, and their accessibility features are currently WIP.

React Native has edge cases for each platform so you'd still need to know each platform carefully.  Plus TypeScript / JavaScript bridging into native can have performance issues.

 Nothing's perfect.

That's all I've got time for today!

Missing: Scala, Clojure, Haskell, F#, Crystal, PureScript, Elm.

2021-01-12

My app uses only java.base, but actually jdeps missed the jdk.crypto.cryptoki module!

I was building a small app and I purposely used only the java.base module.  That way when I build a custom Java Runtime for it, I can build the smallest possible runtime with jlink.

Java's jdeps lets you know which module your app depends on, and it said my app only uses the java.base module. 

Build the runtime with jlink and run my app with it and oops, an error having to do with cryptoki.

Turns out jdeps made a mistake and did not identify the jdk.crypto.cryptoki module!

I suspect my use of the java.net.URI class was to blame, as it was used to access an https URL.

Anyway, if you purposely depend only on the java.base module, but also use the URI class, note that it may actually also depend on the jdk.crypto.cryptoki module!


2021-01-06

Build your own custom Java Runtime with jlink and jdeps

Since Java 9, the way Java programs are supposed to be distributed changed.

It used to be that users would install a Java Runtime Environment (JRE) on their system.  The user then gets the Java app from the app developer.  The Java app then runs on the JRE that's installed.

Some OS like Macs used to even have a "system" JRE installed as part of the OS.

The world's moved on from that style of app distribution.  Nowadays, many users want statically compiled programs, or a single executable file with no dependencies.  For better or worse.

That means when you develop a Java app and want to distribute it, it's on you as the developer to create your own custom Java Runtime and package that runtime together with the rest of your app for distribution.

The upshot is that if you don't use too much of the Java standard library and customize your Java Runtime, then you pay for only what you use in terms of the size of the Java runtime you need to package with your app.

You can find what Java modules your app depends on using jdeps easily: jdeps my.jar

Then you can use jlink to create the Java Runtime your app needs: jlink --output my-custom-runtime --add-modules java.base,and.other.modules

Then you can run your app with the custom Java Runtime: /path/to/my-custom-runtime/bin/java -jar my.jar

The Java Runtime that jlink creates is specific to the OS platform you ran jlink on.  So on Windows, you'll have to substitute java.exe for java in the path above.

There's a way to cross-compile a Java Runtime for a different OS platform with jlink, but you'll need to download the target platform's JDK.  It was easier to run the target platform's OS in a virtual machine and run jlink on it instead.

A thorough and detailed tutorial is How to Create Java Runtime Images with jlink.


2020-12-23

Why upgrade past Java 8?

What reasons are there to use Java versions newer than Java 8 (JDK 1.8)?

I'll give two: (1) security, and (2) great new features!  Let me explain.

(1) security

The last "major release" of Java was Java 9.  The Java upgrade cadence changed at that point.  They're now on a steady stream of "feature releases" model.  Kind of like how there's no major release of Chrome, Firefox, or Windows 10 nowadays... it's just a stream of feature releases.  Java's cadence is 6 months, so about every 6 months, they ship and bump the version number.  JDK 15 was released not long ago.

So who cares?  Well only the current release (JDK15 as of this writing) gets proper maintenance and security updates for free.

Note "for free".  Certainly some software development shops will stick with old JDKs, but they might have good reasons and good mitigation for using old JDKs.  Good reasons like certain customers are stuck (for whatever reason) with old JDKs, or vendors of certain dependencies they need are stuck with old JDKs.

Good mitigations include... paying for paid Long Term Support versions of Java from a JDK vendor.  E.g. it looks like Azul offers a JDK 8 LTS, although the current LTS from any vendor is JDK 11.  The next LTS is JDK 17 scheduled for September 2021 (because of the stream of feature releases model, JDK 12 thru 16 are relatively small feature upgrades, hence the gap between the LTS versions).

You can even use the "free LTS" versions if you want to download those binaries from Oracle, Red hat, or Azul etc., just don't expect actual support without paying!  Mostly they backport security fixes from current version to the latest LTS version, and provide real support for paying JDK LTS customers.  So "free LTS" builds aren't really fully supported for free.  If you must use an LTS release, at least use the latest one (that's JDK 11).

But really, for free security and maintenance, you should just use the latest version.  It's no different from Chrome or Firefox... you should always use the latest to avoid security vulnerabilities.

As for which vendor to choose from? There's Oracle, Red hat, Azul, Amazon Corretto, AdoptOpenJDK, etc.  But they're all built from the same OpenJDK from Oracle, even Oracle's JDK, and they contribute back upstream to OpenJDK.  If you want a slick downloads page and easy installers, AdoptOpenJDK and Azul looks pretty ok.


(2) great new features

Java has a lot of great features in newer releases.  These features aren't just for looks, they bring life back into Java.  I know... "kids these days"... always wanting

  • type inference --- like in Swift, C#, etc
  • lambdas --- like in lisp, C++11, JavaScript, etc.
  • async --- like in JavaScript, rust, etc
  • better garbage collectors --- like Go

But those language features are, rightly or wrongly, table stakes to be considered an up-to-date language nowadays.  Even modern C++ has type inference and async, and smart pointers with garbage collection (reference counted).

So...

  • Java 8 has lambdas
  • Java 10 has type inference (use var)
  • I believe Java 16 will have virtual threads (Java's better answer to async) from Project Loom
  • Java 15 has better garbage collectors (by some metrics), and in fact multiple GCs to choose from for different use-cases: Shenandoah, ZGC, and G1 (admittedly, only more advanced development might care about GC performance)
  • There's multiline text blocks since Java 15, finally
  • There's extended switch expressions that can yield values since Java 14
  • and also a safer switch statement/expression using "arrow case" labels instead of "colon case" labels since Java 14
  • Java 9 introduced JShell, which is fantastic to be able to use a REPL to test out Java code

There's so much to get excited about!  There's a joke about Java being the new Cobol.  But not anymore with features that bring it parity with other languages.

2020-10-21

Quickly Open source code file location in OS's file browser from Netbeans

Want to open the file system browser to the location of a file or folder from Netbeans?

Easy, use the QuickOpener plugin.  Such a basic functionality requires a plugin, whereas it's built in in VS Code, but there it is.

Not the old QuickOpener that's been abandoned.

Use the new QuickOpener that's the fork of the old one (it's a friendly fork).

It works with Apache Netbeans, and can be found in the Netbeans plugin search area at least up to version 11.

For Netbeans version 12, you'll need to download the NBM plugin file from the online page of the new QuickOpener, then install it in Netbeans from the downloaded NBM file.  It worked for me in basic testing, so it should work fine.

2020-06-30

Stop Netbeans downloading whole 1GB maven index

Netbeans for Java programming with Maven likes to download the Maven index that's over 1 GB in size.

There's basically three ways to stop that huge download for 3 different situations:

(1) Temporarily don't want auto updates

If you already downloaded the index, the updates are smaller but still sizable.  One way to to stop it re-downloading the updates is to go to:

Tools > Options > Java > Maven > Index > Index update frequency > set to never.

Then click "Index Now" in that window only whenever you want an update.

(2) Just don't want Maven at all

If you don't have the index downloaded yet (e.g. a new install) and basically never want to use Maven, then in that same window, you could instead check off:

Completely disable indexing

As it warns you, lots of features will be disabled, but if you're not using Maven, who cares?

(3) Want Maven index without the download

This is amazing, there's this plugin you should get and it'll solve this problem: Maven Remote Search plugin.

It's old and it says it's for Netbeans 8.2 (the super old Oracle version), but I tried it out and it works for the new Apache Netbeans 11 and 12!

It lets you search the Maven index online rather than downloading the whole index to your local disk (which is crazy).

The github repo for the plugin is active recently with a historical build, so hopefully it'll get its version bumped and put on the Apache Netbeans plugins web page (which I don't think exists yet...).


Bibliography:

2020-04-10

profile bash_profile bashrc on Ubuntu Linux, Macs, and Windows

I'm trying to get the same script to run and also set the $PATH for the login and non-login terminal shell on all 3 OSs: Windows, Mac, and Linux.

There's 3 parts to making it work:

Does the shell start in login or non-login mode?

  1. Mac's Terminal.app by default starts bash as a login shell.
  2. Ubuntu graphically logs in as a GUI shell (i.e. your desktop environment, gnome-session, etc.), so it's just not bash at all.  But if you then open a terminal in the desktop environment, your manually opened bash shell on Ubuntu may well start as a non-login (i.e. interactive) shell.
  3. Debian LXDE graphically logs in with bash and runs as a login shell by default.
  4. Windows Git Bash (MINGW64) seems to start as a login shell by default as well.

Which script runs on shell startup: .profile, .bash_profile, .bashrc?

  1. Gnome-session or whatever is your desktop environment (DE) is not bash, but the script that starts your DE is supposed to source ~/.profile but NOT ~/.bash_profile.  However, it's possible with Debian LXDE that it'll prefer ~/.bash_profile if it exists over the fallback that is ~/.profile.
  2. Bash as a login mode shell sources ~/.bash_profile and if that's missing then ~/.profile as a fallback.
  3. Bash as a non-login (interactive) shell sources ~/.bashrc and NOT those other two.

Where should scripts go to run on shell startup?

  1. So scripts that run only when your desktop environment or graphical shell starts goes into ~/.profile.
  2. Scripts that run only when you start bash in login mode --- e.g. started with bash -l, via the text console mode in Debian or Ubuntu e.g. via Ctrl + Alt + F2, when Mac's Terminal.app opens, or when Git's MINGW Bash opens on Windows --- goes into ~/.bash_profile.
  3. Scripts that run only when you start bash interactively (non-login mode) --- e.g. started by clicking the terminal icon having graphically logged in to Ubuntu's DE --- goes into ~/.bashrc.  This assumes you didn't configure your terminal emulator to run as a login shell!

Want the script to always run however shell starts up?

Suppose you want a bit of code to run when your shell starts.  It doesn't matter if it's a login, non-login (i.e. interactively started), graphical login from your desktop environment, on Macs, on Windows MINGW, etc.

In principle, I think ~/.bash_profile and ~/.profile both should source in ~/.bashrc as well, so scripts in ~/.bashrc should run whichever way you get your hands into a terminal UI in Ubuntu.  But Macs and Windows Git Bash doesn't have a proper .bash_profile to start.

Thus for Macs and Windows, favor ~/.bash_profile.

For Ubuntu Linux, favor ~/.bashrc.

A single method that works on all 3 platforms would be to put the code in ~/.bashrc, then make sure ~/.bash_profile sources in bashrc.



Bibliography

Why ~/.bash_profile is not getting sourced when opening a terminal?

DotFiles

2020-03-29

Remember Git passwords securely

Typing your password again and again when using git with remote repo is tiring.

Git will integrate with your OS-level password storage easily enough though.

Macs

I'm under the impression that on Macs, it just works.  It's built into git to interface with Mac's Keychain service.

Or else just install Git-Credential-Manager-for-Mac-and-Linux from Microsoft.  You'll need Homebrew, which is a great package manager for Macs for various development tools.  Then follow the instructions: it's super easy.

Windows

Super easy.  Just download and install the .exe for Git-Credential-Manager-for-Windows from Microsoft.

Linux

There's 3 possibilities:

use Git-Credential-Manager-for-Mac-and-Linux

You can install Git-Credential-Manager-for-Mac-and-Linux from Microsoft.  You'll need Linuxbrew, a package manager I've never heard of before today.  Then follow the instructions: it's looks easy.

But this is not my favorite option because:
  1. never heard of Linuxbrew

  2. MS Git-Credential-Manager sends telemetry to Microsoft.  Not much telemetry data, and I'm trusting of MS more or less, but if you're using Linux, I'm going to guess you might see "telemetry" and "MS" and wonder why it's needed for using Git.

  3. someone's tried it a year ago and it didn't work

use Libsecret (best option)
It's 3 lines of commands to run:

sudo apt-get install libsecret-1-0 libsecret-1-dev
sudo make --directory=/usr/share/doc/git/contrib/credential/libsecret
git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret

This saves your credentials encrypted in ~/.local/share/keyrings.

Yes, you're downloading and compiling it yourself as it's not built-in... in 2020. Crazy.  But is apparently still The Way to go with Ubuntu or Lubuntu 19.10.

Libsecret should interact with the OS level password store.  On Ubuntu, that would be gnome-keyring.  If you need to manage the keys stored by gnome-keyring, you'll need to install another tool like the GUI utility Seahorse:

sudo apt-get install seahorse



use the built-in store (totally insecure and NOT encrypted)

Totally built-in.  Nothing to install.  One line to set up:

git config --global credential.helper store

It stores your passwords in PLAIN text in a file in your home directory.  Don't do this.  Anyone can read your password.  I can read your password.  So don't do this.

2020-03-25

One way to use SemVer for software versioning

Everyone has their own system for versioning in their software development process.

Semantic Versioning is very popular of course.  It basically takes the form of:

major.minor.patch-prerelease+build
  • Major for breaking changes.
  • Minor for backwards compatible feature additions.
  • Patch for bugfixes meant to be backwards compatible.
  • Pre-release tags for internal versioning, building up to a release.
  • Build for internal build numbers, useful if you build a lot.
There is a precedence order to SemVer numbers to essentially designate which version is "newer" in some sense.

Pre-release and Build tags do not participate in the precedence order.   So when specifying ranges of versions, those pre-release tags are kind of ignored in things like NPM contrary to precedence order as SemVer defines it --- reality is different than theory, right?

Pre-release tags are great for Designating development stages but when they are removed to do a non-pre-release release (e.g. a final release), it messes up the lexicographical ordering --- i.e. messes up the order in which they show up in my file browser. haha.

How I use SemVer

So I'm trying a SemVer compatible system that respects the 2nd and 3rd laws: "Don’t mess with math", and "Make friends with infinity.  In other words, don’t be afraid to increment".

The basic idea is: keep pre-releases to a patch level, release at the next patch level.

The rules are:
  1. Start at 0.1.0.
  2. Increment patch number to make a release
  3. Increment major, minor, or patch (whichever you're targeting for next) to make pre-releases
  4. Pre-releases are tagged a1, a2, ..., b1, b2, ..., rc1, rc2, etc.
  5. Use increasing build numbers if you need
  6. Use a "." then another build field if you must, like for adding a non-increasing alphanumeric build field (e.g. a SHA)

Example

So you'll get versions of WildApp like these, e.g.:
  • WildApp 0.1.0-a1
  • WildApp 0.1.0-a2
  • WildApp 0.1.0-b1
  • WildApp 0.1.0-b2
  • WildApp 0.1.0-b3
  • WildApp 0.1.0-rc1
  • WildApp 0.1.0-rc2
  • WildApp 0.1.1
  • WildApp 1.0.0-a1
  • WildApp 1.0.0-a2
  • WildApp 1.0.0-b1
  • WildApp 1.0.0-b2
  • WildApp 1.0.0-b3
  • WildApp 1.0.0-rc1
  • WildApp 1.0.0-rc2
  • WildApp 1.0.1
I'm going to give it a try at least.

2019-10-18

Why are these 3 open source licenses best?

I've already said that these 3 licenses are best for open source nowadays for me:
  1. Apache License 2.0 --- Apache-2.0 @ SPDX, ChooseALicense
  2. Mozilla Public License 2.0 --- MPL-2.0 @ SPDX, ChooseALicense
  3. GNU General Public License 3.0 or later --- GPL @ SPDX, ChooseALicense
But WHY??

The open source community has basically settled on 4 styles of sharing, and with it 4 genres of open source licenses.  The list above is my pick of what I think is best in each of the first 3 genres.  Let's talk about each one in turn:

1. Software developers are free to do whatever they want with my code in building their software.

These licenses are about the freedom of the developers of the software.

Look for MIT, BSD, Apache licenses, etc.

But I think Apache License 2.0 is the best because

2. Users are free to do whatever they want with my code and any modifications to my code in the software they received, even if they can't with the software's other proprietary code.

These licenses are all about the freedom of the end users who have the software, in a piecemeal fashion.

Look for MPL, EPL, LGPL licenses, etc.

But I think MPL 2.0 is best because
  • compared to MPL and EPL, the LGPL basically makes the distinction that static linking of code equals modifications to that code, but dynamic linking is not.  That just seems like a needless distinction for a license to make, and MPL and EPL doesn't make that distinction.  And I like static linking.
  • EPL 2.0 is basically a very new update to EPL 1.0 that makes the EPL even more complicated than it already was.  The main purpose was to (1) change the boundary of what counts as "my code" from a module based distinction to a file based distinction, which is what the community has standardized on, (2) make it more internationally usable, and (3) add in GPL compatibility as an opt-in.

    Unfortunately, GPL compatibility is opt-in and not default making it even more complicated when mixing EPL 2.0 with/without GPL secondary license, and EPL 1.0 code which was never GPL compatible.

    So if your community has settled on EPL (like many in Java or Clojure), then maybe sticking with what the community is using is easiest.  Otherwise, it's hard to make an informed use of the EPL as an individual, unless you've got lawyers on retainer... which is maybe why the EPL is very well regarded by businesses?
  • It is compatible with Apache 2.0.
MPL 2.0 on the other hand has GPL compatibility by default, unless opted-out of.  It's much older so it's better known and understood, still very well regarded, and used by large projects like Mozilla for Firefox, Adobe for Flex, LibreOffice, etc.  And it's relatively short and easy to understand, so MPL 2.0 it is! 


3. Users are free to do whatever they want with all of the code in the software they got from the software developers.

These licenses are all about the freedom of the end users who have the software, not about the developers'.

Look for GPL.

This is the classic "viral copyleft" thing, although talking about strong/viral copyleft is kind of more confusing than helpful (see Weak or Strong is Wrong) because, philosophical discussions aside, it's really just about what kind of code sharing you want to take place with your code you authored.

Having said that, if you incorporate MPL 2.0 or Apache 2.0 code into a GPL code base, the whole code base has to be distributed as GPL moving forward.


4. Users are free to do whatever they want with all of the code in the software they use from the software developers.

These licenses are all about the freedom of the end users using the software over a network.

Look for AGPL, SSPL, etc.

GPL had a SaaS loophole / ASP loophole:  what happens if the end users never got the software, because they only used it running in the "cloud" (i.e. on computers they don't own)?

AGPL is supposed to close that loophole so that if an organization modifies AGPL software, any end user using that AGPL software in the cloud must be able to do anything they want with its' code.

More recently, AGPL was found to have a no-modification loophole: what happens if an organization just uses and doesn't modify AGPL software?  The AGPL doesn't compel code sharing in that case!

So companies could containerize AGPL software, build an API around it to use it internally, etc., and as long as they never modify the actual AGPL software, then they could use without ever sharing any code.

Some patched that loophole with the Commons Clause.  MongoDB took a different path by creating the SSPL.

I don't know enough about this genre of sharing to suggest any license as best.  Reading SSPL Was Not Commons Clause, it's clear this is still cutting edge licensing legal stuff.  If you're looking for a license for this genre of sharing for any serious work, you'd probably have your own lawyers anyway.

And I'm definitely not a lawyer, so let's just agree to take this as entertainment.  :)

2019-10-12

These 3 licenses are best for Open Source


Choosing an open source license is confusing.  There's so many!  But I've narrowed it down to the 3 best ones for me nowadays.

I'm not a lawyer, so take this as entertainment.  :)
  1. Apache License 2.0 --- Apache-2.0 @ SPDX, ChooseALicense
  2. Mozilla Public License 2.0 --- MPL-2.0 @ SPDX, ChooseALicense
  3. GNU General Public License 3.0 or later --- GPL @ SPDX, ChooseALicense
Which you use depends on what kind of sharing you want to do.

Use Apache 2.0 license if:
    1. you want anyone to be able to use your code however they want
    2. including building bigger projects based on your code with the bigger work licensed however they want (including possibly "all rights reserved" proprietary licensing), 
    3. without expecting them to share anything back in return,
    4. without expecting them to acknowledge they used your code,
    5. and without expecting them to share your code that they used.
Use MPL 2.0 if:
    1. you want anyone to be able to use your code however they want,
    2. including building bigger projects based on your code with the bigger work licensed however they want (including possibly "all rights reserved" proprietary licensing),
    3. but you expect that any changes they make to your files are shared back in return,
    4. you expect that they will acknowledge they used your code,
    5. and you expect they'll make available your code that they used.
Another great thing about MPL 2.0 and Apache 2.0 is that they are compatible with each other!
Use GPL if:
    1. you want anyone to be able to use your code,
    2. including building bigger projects based on your code but with the bigger work also GPL licensed,
    3. and any changes they make to your code, as well as any code added to your code even if in other files or modules, are all shared back in return,
    4. you expect that they'll acknowledge they used your code,
    5. and you expect they'll make available your code they used, and also any code they add, even if they added them in other files or modules.
Notice the big distinguishing point is in how much you want users of your code to share back changes or additions.  From zero sharing required (Apache 2), to sharing changes or additions to your files (MPL 2), to sharing all changes or additions whether they be in your files or in other files linked (dynamically or statically) to your files (GPL).

If you want help navigating to which license to use, this License Selector I found the best (except it's missing EPL-2.0).  While the Choose a License site from GitHub has the slickest UI, they make some questionable suggestions from my point of view, like highly suggesting the MIT (a.k.a Expat*) License instead of the Apache license.

One reasonable advice from ChooseALicense though is to choose the license common in the community you want to share in, but that's really only true if there is an equivalent license to what you'd otherwise want to use.

For example, if you want to use the MPL-2.0, but you're wanting to share in the Clojure community, then you're probably better off using the EPL-2.0 (Eclipse Public License) instead, despite the EPL 2 being very likely not as "good" as the MPL 2 by some metrics.  But EPL is only even an option as it's basically equivalent to the MPL as far as I could tell.

If for some reason you really wanted to GPL license your Clojure code, then EPL isn't going to cut it anyway because they're not at all equivalent.
    * this is different from the MIT / X Consortium License.

    2019-07-23

    Create React Native App using TypeScript compiled with Babel


    Here's every step to creating a React Native app, written in and type checked with TypeScript, but compiled with Babel 7.

    You're assumed to have some proficiency with using the terminal.

    Tools to Install on your PC

    I'm on a Mac, but the steps shouldn't be much different on Ubuntu Linux or Windows.
    1. Install Homebrew.
      It's a package manager for Macs, and Linux too, but if you're on Ubuntu or Debian, I suggest just use the built-in one like APT.
    2. Install Node thru brew.
      Installing Node should include the NPM package manager with it, which is needed below.
    3. Some say to next install Yarn thru brew.
      Yarn is yet another node package manager... you can skip this if you want, it's not strictly needed, and I won't use Yarn in this tutorial.
    4. Fix NPM if you installed Yarn.
      I actually mentioned Yarn at all only because with Homebrew, you might now need to fix the Node NPM install as it might be broken by installing yarn.  Simply[1] run: yarn global add npm.  Remember, we don't need Yarn in this tutorial though.
    5. Install React Native CLI thru npm:npm install -g react-native-cli
      This is basically a template that creates a RN project for you to start from.
    [1] https://stackoverflow.com/questions/33870520/npm-install-cannot-find-module-semver/49422151#49422151

    New Project with React Native

    1. Create a React Native project:
    $ react-native init CoolProject

    1.5. Upgrade core-js:
    I got an error like this:
    warning react-native > create-react-class > fbjs > core-js@1.2.7: core-js@<2.6.8 is no longer maintained. Please, upgrade to core-js@3 or at least to actual version of core-js@2.

    You just need to upgrade core-js.  Go into your CoolProject folder and run:
    npm install --save core-js@^3

    2. cd CoolProject to go into the new project's root directory, then install Babel:
    $ npm install --save-dev @babel/core @babel/cli
    I assume this will install for you Babel 7 or above.  We'll need it later to migrate to TypeScript.

    3. Create a lib folder in the project root.
    $ mkdir lib
    The lib folder will be used to contain the App's JavaScript files. Traditionally, the folder would be called "src", but looking forward, these JavaScript files eventually will be produced by the TypeScript compiler for us.

    So instead, we're going to reserve "src" for later when we migrate to TypeScript, instead of calling it "src" right now when we're still dealing with just JavaScript JS, or JSX, files.

    Separating the TypeScript and the compiled JavaScript files is a technique to avoid in-source builds (a usual technique used in compiled languages like C++: see e.g. in-source vs out-of-source builds).

    2019-03-25

    Book Review: The JavaScript Handbook by Flavio Copes

    If you're a competent programmer but have been away from JavaScript for some time and want an extremely brief overview of the updates to JavaScript, in bite size form, then this book (The JavaScript Handbook) is for you.

    Flavio goes over all the new features in JavaScript from ES6 to ES2018. Listing out each feature or change and giving an extremely brief description of it.

    This book reads like a collection of very short blog posts though. And would benefit from more editing, both on a sentence level and on an overall topic cohesiveness level. That's a small nitpick in what is overwise a good set of writing.

    There's definitely two major parts to the book. First part is extremely brief in describing the changes in each version of ECMAscript.

    Second part basically goes over all the changes, again, but this time in more detail. Not a lot more, but sufficient for a competent programmer to know enough.

    E.g. if you don't know that much about async and promises on a conceptual level, this book isn't going to teach you enough to really use those features productively.

    Or if you don't really know the problems with the "this" keyword in JavaScript, the book's description of it in relation to how it works and how it's changed with arrow functions isn't the most enlightening. Some sentences on it, superficially without a deeper understanding of how programming languages work, are downright contradictory sounding.

    All in all though, I went through it cover to cover, and the book does a good job for reviewing changes to JavaScript for the competent programmer.

    For next to free (i.e. email signup), it's hard to beat.

    2019-03-23

    Homebrew blew up from libffi, ruby, or? Here's how to fix it.

    I wanted to upgrade Homebrew itself and what it installed on my Mac.  It's probably been over a year, so lots of outdated stuff.  I ran:

    brew update
    brew upgrade

    Everything looked good, and it gave me some instructions to add to my PATH the Homebrew installed ruby and ruby gems along the lines of adding the following to my .bash_profile:

    PATH="/usr/local/opt/ruby/bin:$PATH"
    PATH="/usr/local/lib/ruby/gems/2.6.0/bin:$PATH"

    So I did that, and tried out ruby, irb, and emacs. It starts spitting a bunch of different errors, and I probably confounded the causes and errors as I was trouble shooting, but it gave me errors about:

    dyld: Library not loaded: /usr/local/opt/libffi/lib/libffi.6.dylib

    Especially when I ran emacs, I got:

    dyld: Library not loaded: /usr/local/opt/libffi/lib/libffi.6.dylib
    Referenced from: /usr/local/opt/p11-kit/lib/libp11-kit.0.dylib
    Reason: image not found

    And when I ran irb, I got:

    Traceback (most recent call last):
    2: from /usr/local/opt/ruby/bin/irb:23:in `<main>'
    1: from /usr/local/Cellar/ruby/2.6.2/lib/ruby/2.6.0/rubygems.rb:302:in `activate_bin_path'
    /usr/local/Cellar/ruby/2.6.2/lib/ruby/2.6.0/rubygems.rb:283:in `find_spec_for_exe': can't find gem irb (>= 0.a) with executable irb (Gem::GemNotFoundException)

    Hmm?  I tried a bunch of stuff and it probably made it more confusing, especially as I started running brew and got errors like:

    /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- active_support/core_ext/object/blank (LoadError)

    Or when I ran brew help and got:
     
    /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- mechanize/version (LoadError)

    Well now I just wanted to "start over".  You could do that by deleting all of Homebrew and what it installed, to start fresh, but what a pain!  I'd have to know what packages to re-install with a new Homebrew installation and go through installing those one by one.

    Instead, the easier thing to try first is this:

    1. Remove from your PATH the brew installed ruby and rubygem

    On Macs, it's probably "safest" when troubleshooting Homebrew to use the Mac OS X's original Apple installed ruby.  Yes, it's an old version of ruby, but when I changed my PATH to use the Homebrew installed ruby, I probably made troubleshooting much more difficult.  It's tough enough to troubleshoot Homebrew, but if brew (a ruby program) runs on the brew installed ruby, brew just might be running on a ruby that brew screwed up...

    2. try brew update-reset

    This fetches and resets Homebrew and all tap repositories to the latest from their git repositories.  In other words, restart them fresh.

    3. brew reinstall libffi

    emacs, ruby, python, and a bunch of other stuff needs it.  Even if you've got it installed.  Reinstall it to make sure it's installed right.  It fixed emacs for me for sure.  Probably ruby too.

    4. update and upgrade brew again

    brew update
    brew upgrade

    And that finally fixed it.  I added the ruby and gem paths back into PATH in my .bash_profile, and installed irb as a ruby gem (Mac OS X has old versions of both ruby and irb pre-installed, but getting the latest means installing ruby via brew and irb as a ruby gem).

    2019-03-20

    How to migrate ReactXP App to make an Electron Desktop App

    ReactXP comes with a few sample apps. The TodoList app is the most developed and closest to the structure of a production app written in TypeScript, but doesn't target Electron.  Here's how to migrate it to run on Electron so it could work as a desktop app.

    I'm on a Mac, but Electron is cross platform with Windows and Linux so it should work there too.  ReactXP 1.6.x is current right now (hilariously, it upgraded from 1.5.x just as I was working on this and it introduced some new error so this migration is totally in beta).

    Strategically, the idea is ReactXP can already directly target the web via React (in addition to native iOS, Android, and Windows via React Native).  So we just target the web with ReactXP, modify the web page loader to run in Electron, and modify some code so it doesn't assume it's running at the root of a web server.

    Make TodoList into a web app. Run it on a local dev web server

    First, use git to clone the ReactXP repository:

    git clone https://github.com/microsoft/reactxp

    In the reactxp/samples directory, you'll find the TodoList app. Open it:

    cd reactxp/samples/TodoList/

    Install Electron into it:

    npm install --save-dev electron

    Now we need a web page to load the app into Electron.  We'll use the default one from Electron quick start.  Just copy the main.js from electron-quick-start into the TodoList folder, and rename it to electron-main.js just for clarity.

    2019-03-09

    Maximally Cross Platform with React?

    Once upon a time, going cross platform meant making an application that ran on both Macs and Windows, and maybe Linux/Unix if you're into that crowd.

    Now everything is fragmented and compartmentalized.  Consider what "platforms" there are now for making apps:

    1. desktop web browsers --- i.e. Chrome, Firefox, Safari, vs Edge --- further divided by Macs, Windows, and Linux (at least for browsers that are cross platform, ha!)
       
    2. mobile web browsers --- i.e. iOS vs Android --- further divided by phone vs tablet display sizes
       
    3. Electron desktop apps --- i.e. Macs, Windows, vs Linux (built with web browser technology)
       
    4. native desktop apps --- i.e. Macs, Windows, vs Linux
       
    5. native mobile apps --- i.e. iOS vs Android --- further divided by phone vs tablet display sizes
    Some of the above differences are a matter of building user interfaces sized for use on that display size, while other differences are a matter of framework (e.g. Cocoa vs Android SDK).

    But at worse, that's still 26 different platforms to build for!  There are technologies that help bridge the divide, and one family of such tech is called React.

    Here's a survey of some React based technologies and which platforms they work well for.


    2019-03-06

    Create React Native App using TypeScript with Babel and Expo

    (updated 2019-07-23 for clarity)

    Here's every step to creating a React Native app (with or without Expo), written in and type checked with TypeScript, but compiled with Babel 7.

    You're assumed to have some proficiency with using the terminal.

    Tools to Install on your PC

    I'm on a Mac, but the steps shouldn't be much different on Ubuntu Linux or Windows.
    1. Install Homebrew.
      It's a package manager for Macs, and Linux too, but if you're on Ubuntu or Debian, I suggest just use the built-in one like APT.
    2. Install Node thru brew.
      Installing Node should include the NPM package manager with it, which is needed below.
    3. Some say to next install Yarn thru brew.
      Yarn is yet another node package manager... you can skip this if you want, it's not strictly needed, and I won't use Yarn in this tutorial.
    4. Fix NPM if you installed Yarn.
      I actually mentioned Yarn at all only because with Homebrew, you might now need to fix the Node NPM install as it might be broken by installing yarn.  Simply[1] run: yarn global add npm.  Remember, we don't need Yarn in this tutorial though.
    5. Install Expo thru npm:npm install -g expo-cli

      Expo is apparently kind of a mini-platform for running React Native apps, and makes it easy to test a React Native app on devices through its Expo app, or on Macs and PCs without resorting to an Android or iOS emulator (which is great, because those emulators are dreadfully slow in my experience).  Plus installing Expo should include React Native with it.
    6. Install React Native CLI thru npm:npm install -g react-native-cli
      This is basically a template that creates a RN project for you to start from.
    [1] https://stackoverflow.com/questions/33870520/npm-install-cannot-find-module-semver/49422151#49422151

    New Project with React Native or Expo

    1. Create a React Native project (possibly thru Expo):
    $ expo init CoolProject
    or alternatively without Expo:
    $ react-native init CoolProject

    1.5. Upgrade core-js:
    I got an error like this:
    warning react-native > create-react-class > fbjs > core-js@1.2.7: core-js@<2.6.8 is no longer maintained. Please, upgrade to core-js@3 or at least to actual version of core-js@2.

    You just need to upgrade core-js.  Go into your CoolProject folder and run:
    npm install --save core-js@^3

    2. cd CoolProject to go into the new project's root directory, then install Babel:
    $ npm install --save-dev @babel/core @babel/cli
    I assume this will install for you Babel 7 or above.  We'll need it later to migrate to TypeScript.

    3. Create a lib folder in the project root.
    $ mkdir lib
    The lib folder will be used to contain the App's JavaScript files. Traditionally, the folder would be called "src", but looking forward, these JavaScript files eventually will be produced by the TypeScript compiler for us.

    So instead, we're going to reserve "src" for later when we migrate to TypeScript, instead of calling it "src" right now when we're still dealing with just JavaScript JS, or JSX, files.

    Separating the TypeScript and the compiled JavaScript files is a technique to avoid in-source builds (a usual technique used in compiled languages like C++: see e.g. in-source vs out-of-source builds).

    2017-04-02

    Which programming language to teach? A principled choice

    Java is still one of the most popular programming language to teach the young or newbies to programming.  It's a very problematic choice.  See:

    1. The problem of Object Oriented Programming is an education one
    2. Learn Python instead of Java as your first language [1]
    3. Java has deep expression problem for beginning students
    4. Governments mandating which programming language to teach
    Having looked at some of the problems and issues before, I want to be constructive and offer some principles for how to choose which programming language to teach instead.


    1. Principle of no magical incantations

    This can be viewed as a language feature vs. library supplied functionality issue.  The latter is mostly okay, but the former should have no magical incantations that students need to learn.

    Magical incantations are language features like the "static" keyword in Java.  You could imagine a student asking:
    What does "static" mean in the "main" method declaration?  Why is it "static" and not something else?  What does "static" do?  Why do I need to write "static" when I don't know what it means to make something "static" vs not static?  I don't want it "static", I want it "grounded", can I write that instead?
    Now you could imagine a teacher answering:
    The answer to why, what, how, etc., would require explaining OOP and how it's implemented in Java and...  Look, just write 'static'.  It's just the way it is.  Just do it this way or else it doesn't work.  It's a magical incantation the Java gods require you to recite.
    The more language features required to write even a simple program, the more things students have to learn or be ignorant of but use (i.e. magical incantations).

    That makes it more likely that students have to learn everything about the programming language all at once just to do even the simplest thing at all.

    2. Minimize concepts required to start doing stuff

    The more that foundational programming concepts are required, the more programming language specific things students must think about whilst at the same time thinking about solving the actual (i.e. non-programming-language, "business") problem in its problem domain.

    Otherwise, it'd be like students have to learn everything in the programming language all at once in order to do anything at all.

    3. Idiomatic code from the start

    A programming language that encourages writing idiomatic code from the very start reduces either the magical incantations required in writing idiomatic code, and reduces dumb code written stupidly just to avoid learning the magical incantations.

    Otherwise, students have to learn everything all at once just to do anything at all.

    Magical incantations are not hidden machinery!

    I'm not advocating learning C because it doesn't have the magic of a garbage collector (GC)!  The GC is not a magical incantation: it's mostly hidden machinery.

    Hidden machinery works quietly, never broadcasting its own existence, and isn't in your face about what it does.  It doesn't offer an affordance where none is called for, or where it would be inappropriate for the target user.

    Magical incantations are flamboyant and in your face.  You know it's doing work because the magician is showing you that work is being done through the incantations, even if what's shown and what's done might very well be disconnected from each other.

    To a newbie, the "static" Java keyword (to pick on something as an example) is magical incantation.  The Java GC is hidden machinery that we should be thankful for having every day.

    Hidden machinery that makes a language easier for a newbie to learn is great.  Magical incantations that calls out its own existence is not helpful.

    Magical incantations are also not Spooky Action at a Distance.  But that's a topic for another day.


    Afterword
    I wrote the above years ago (around 2013 February 4), and only now slightly expanded and lightly edited it to share here.

    [1] I don't recommend learning Python as your first language anymore for various reasons (but I'd certainly recommend learning Python over Java as your first language!).