loading...

Something I've come to appreciate about dynamic typing

twitter logo github logo ・1 min read

I'm a fervent devotee of static type checking; it cuts debugging time in half, makes me feel safer, and almost everything it prevents you from doing is something you shouldn't. Or, that's what I used to think.

Lately I've had some experiences in my job where we got great use out of the ability to access properties of a Javascript object based on a variable, for example, this.customer[field] could access whichever field of the customer we needed without a separate branch. In Go, we might've had to write:

switch field {
case "email":
    callFunc(customer.email)
case "phone":
    callFunc(customer.phone)
}

... or something so ugly. The cleaner solution is only possible because Javascript doesn't check the types of objects or fields at compile-time. As far as I can tell, there's no way to reconcile the two.

I still favor static typing, but I see it as less one-sided now.

twitter logo DISCUSS (14)
markdown guide
 

The cleaner solution is only possible because Javascript doesn't check the types of objects or fields at compile-time.

It's arguable if it's cleaner, but it's definitely shorter. That's not surprising though, as you just moved type handling and verification from compile into run time, i.e. if different properties return different types, you now have to switch on type elsewhere in your code-base.

Let's look at the following OCaml example:

type person = {name: string; age: int}
type person_info = Name of string | Age of int

let get person = function
  | "name" -> Ok (Name person.name)
  | "age" -> Ok (Age person.age)
  | s -> Error "invalid field"

Is this longer than your JS one-liner? Certainly. But it already embeds the knowledge on whether or not the operation succeeded in the Result type, something that would end up as null check in the JS. You also already get all type information on possible returned values, including exhaustiveness checks on the match. Is this better? Maybe, that depends on your point of view. But the reason it's longer than your JS one-liner is simply that it does more, most of which (like null-checking) you'd hopefully also do elsewhere in the JS solution.

Usage:


let p = { name = "Baby Yoda" ; age = 50 }
get p "name"
- : (person_info, string) result = Ok (Name "Baby Yoda")
get p "powers";;
- : (person_info, string) result = Error "invalid field"

If you match on the result of get, the compiler would urge you strongly to also handle the unhappy path if you don't add a clause for Error:

Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
Error _

BTW, I say this as someone who spent most of his working life in dynamically typed languages (Ruby and Python mostly). Also Go doesn't have a particularly sophisticated type system, so solutions get ugly fast.

 

Thank you for the response. I guess my example wasn't a very good one. We have situations like a <select> in the UI that controls which field to sort customers by, so we knew all the values were valid, and didn't have to do null-checking or error-handling.

I guess I wouldn't know if other statically typed languages have better solutions. Go, Haskell, and C are the only statically typed languages I know (and mostly Go).

 

I get it, there really are some situations — especially in web development — where typing can become painful (looking at you, JSON).

When writing Go one has essentially two options: mourn the relatively primitive type system and try to find clever workarounds or embrace Go for what it is and write the explicit slightly longer version. The correct answer is almost always the latter, as it's pretty much what the language designers expect you to do.

 

When you have a this.customer[field] which is an enum of values how can you apply polymorphism here? You will need more abstractions. What you can do is just don't accept the JSON as it is but implement a custom Parser:

jsonformatter.io/golang-json-parse...

So in the end the customer.email and the customer.phone would be custom fields and then you can do something like:

type field interface {
    Handle ()
}

type EmailField struct {
    s string
}

type PhoneField struct {
    p string
}

func (email *EmailField) Handle()  {
    // Code to callFunction or whatever
}

func (phone *PhoneField) Handle()  {
    // Code to callFunction or whatever
}

Just an idea.

 
 

Ooh, I'm not sure. Basically having an enum type for the properties of the object?

I guess that could probably work. Most languages I've seen with static typing don't have a syntax for accessing a struct field with a variable, rather than the literal field name, but I'd like to see one that does.

 

I agree, this ain't a normal types thing, but I think it can be done (Rust? C++?) and I will probably waste time on this.

 

In java, you can pass the field similar like javascript though it static type

 
 

Maps in statically typed languages require all the values to be the same type, though, unlike structs.

 

That's only a problem if a language doesn't have sum types. If we use OCaml again, with the person_info discriminated union above, it's easy to make a map with different types:

type person_info = Name of string | Age of int
module Person = Map.Make(String)
let p = 
  Person.empty 
  |> Person.add "name" (Name "Baby Yoda") 
  |> Person.add "age" (Age 50)
Person.find "name" p
- : person_info = Name "Baby Yoda"
Person.find "age" p
- : person_info = Age 50

Ooh. I don't know OCaml, but that looks interesting. I guess I could probably implement something similar in Haskell.

Exactly the same principle in Haskell:

import qualified Data.Map.Strict as Map

data PersonInfo = Name String | Age Integer

m = Map.fromList [("name", Name "Baby Yoda"), ("age", Age 50)]
Map.lookup "name" m
Just (Name name) = Map.lookup "name" m
name
-- => "Baby Yoda"
 

Some static languages have reflections and libraries for dynamism where you need them.

Scala have dynamic trait.
Haxe have dynamic types.
Java have reflection and libs to help.
...

Classic DEV Post from Jul 30 '19

When Stack Overflow Goes Offline...

Ryan Westlund profile image
I'm a programmer, writer, and philosopher. My Github account is yujiri8; all my content besides code is at yujiri.xyz.