JavaScript weird type system - Boxing

Aloha, it's nice to have you again in another article, in this one we'll be talking about one concept that exists withing JS but probably mis-understood and this misunderstanding leads to inaccurate mental models.

Boxing

So what is boxing? well let me define boxing according to how I understand it.

Boxing is more like wrapping some value of type of x in a container that is type of y which has more functionalities that x can use - Some panda

Hopefully I did great job getting you confused, I think that's what academic definitions are all about after all 🥲

But let's break down this definition, let's assume that we have some value of the type x, for simplicity, let's say that x is the well known type string, so our value may be name = 'ahmed osama'

So that's the first part of my definition, that we assume we have a value of some type, moving on to the second part.

We want to wrap this value 'ahmed osama' of type string in some other container of type y to have more functionality we may need in our string type.

So what about putting our string value in an object container maybe?

And what about we choose the array object to wrap our object within?

I hope you're getting a sense of where are we going with this, but lemme help you.

I presume you have gone through the following code snippet over billions times, ever though how tf this is possible to treat strings as objects?

let name = 'Ahmed osama'

console.log(name.length) // 11
console.log(name.includes('e')) // true

Everything in JavaScript is an object

One of the most famous sayings in JS and I truly don't know who came up with this one, rumor has it that Douglas Crockford stated this.

Whomever said this quote is probably wrong, at least in the abstract sense without anymore clarifications.

But, he still has a point, probably whomever stated this quote, he was speaking in the sense of prototypes, as known in JS, most of the primitive data-types can be constructed using their object-ish constructors.

let myTrue = new Boolean(true)
let name = new String('ahmed osama')

console.log(typeof myTrue) // object
console.log(typeof name) // object

And surely that makes everything in JavaScript an object, but once again, this definition or quote has some ambiguity around it, does that mean that the letter a itself is an object of type String or the identifier name itself is the object and it contains within it some primitives?

Does that mean that JavaScript doesn't have the primitive data types within it?

And many other questions may rise due this conflict.

And sure, JavaScript nor any other programming language can work without having the primitive data types within it, how would you store your data if that happened?

All known data - as far as I've seen - yield down to become strings and numbers, I'm not talking about how they're stored in our application/memory, yes that may vary between many options: arrays, hash-tables (jsons), binary tree and many other options.

But whenever you simplify it to the extreme, what you end up with is a bunch of strings and numbers to represent some user, maybe details about him like his SSN, phone number, name, street or whatever you may accept.

And once again, I am not making this up of my own, ES specs itself stated that JavaScript must contain primitive data types and sorted them in the well known list of string, number, undefined and the other non-object ones.

So I may re-quote it in a more applicable one - atleast in my point of view -

Primitive data types in JavaScript are not objects, but can be treated as if they were ones due to boxing, which gives a feeling that everything in javascript is object-like.

I belive this definition is more accurate and doesn't leave any ambiguity behind, hopefully.

So, let's revisit my definition of boxing, it's all about wrapping some value of type x in another type y and it should behave as if it was of type y a.k.a has the functionalities of y.

That's exactly what happened when we accessed name.includes('e'), the string ahmed osama was wrapped in another type that has this method includes.

How lucky, we have two types within JavaScript that have this method in their prototype, Array and String (The capital initial means the constructor version)

const someString = new String('abc')
const someArray = new Array(15)

console.log(someString.__proto__)
console.log(someArray.__proto__)

If you run this code snippet in some browser environment (node didn't work for me it showed soem non-helpful representation), you may find something that looks like that.

Note: Click here for string prototype or here for array prototype

An array of all the possible methods with the String.prototype, so your primitive string is some how turned into it's object form so you can acess these methods.

Let's have another example of wrapping your types into another containers so you have more functionality over them.

In function programming there exists some concept called Monad which is some how close to what we're discussing.

Let's say that we want to create a container for any value and support it with some more functions, I'd go with something like the following.

// First lets define the acceptable types
type Primitive =
  | string
  | number
  | boolean
  | undefined
  | null

type JsonArray = Json[]
type JsonObject = { [k: string]: Json }
type Json = JsonObject | JsonArray | Primitive

// Next, let's define our Box

const Box = <T extends Json>(x: T) => ({
  map: <U extends Json>(f: (v: T) => U) => Box(f(x)),
  filter: (f: (v: T) => boolean) =>
    f(x) ? Box(f(x)) : Box(undefined),
  fold: (f: (v: T) => T) => f(x),
})

So what's this we are building?

Well, we're just making a function that accepts any type that's not a function, because that's what a JSON type is anyway, and returning an object that contains multiple functions/operations that operate against the given value.

And for the sake of simplicity, I just included map and filter operations within the box, surely you can provide any desired operation.

And the fold operation is just a function to extract out the value after manipulation outside of the box.

So let's use our box to manipulate some value.

let myName = Box('ahmed osama')
  .map((v) => v.toUpperCase())
  .map((v) => v.split(''))
  .map((v) => v.sort())
  .map((v) => v.join(' '))
  .map((v) => v.trim())
  .fold((x) => x)

So what are doing here?

  • Well first, we're creating a box around the value 'ahmed osama' so we can have more functionality.

  • Then we're mapping the string to the uppercase form of it, note that this map function we have is a little bit different the regular map function that's provided within the language, as it doesn't wrap the output in an array, it returns it with the same type provided.

  • Afterwards, we're once again mapping the returned caplitalized string to split it into its characters

  • We're sorting the array of characters so we have my name in sorted chars.

  • We're joining it back to a string separated by spaces

  • Them we trim the output as we have trailing spaces

  • Finally we fold the ouput a.k.a extracting it out of the box by applying the identity function

console.log(myName) // A A A D E H M M O S

We ended up having my name split into sorted uppercased characters.

Some naive process yeah but hopefully you get the idea behind it, we wrapped the primitive string in an object container that has mutliple functions that can operate against this string type to ahcieve something.

And that's simply Boxing :D

Now that's done with, I'll see you in the next part ^^

Have a nice drink and a wish you a very pleasing day, Cheerio 💜

Consider supporting/following me

21