Dynamic Typing Flexibility: File#read

Did you know, Ruby will take literally anything when you’re trying to open a file? It doesn’t care about the type, it just cares if it can be treated as a string. Let’s look, and assume hello.txt is a file with just the string “hello” in it:

File.read("hello.txt")
# => "hello"

File.read(Pathname.new("hello.txt"))
# => "hello"

class ATadContrivedSure
  def to_s
    "hello.txt"
  end
  alias to_str to_s
end

File.read(ATadContrivedSure.new)
# => "hello"

And if you give it something that can’t be coerced into a string (doesn’t define a to_str method), it’ll blow chunks at runtime:

File.read(true)
# => TypeError (no implicit conversion of true into String)

# because we can, for now 👇
class TrueClass
  def to_str
    "hello.txt"
  end
end

File.read(true)
# => "hello"

Here’s the types of designs I imagine Ruby + static typing will move us to (it obviously won’t actually change File#read):

class File
  def self.typed_read(pathname)
    raise unless pathname.is_a?(Pathname) # faked type check
    read(pathname)
  end
end

# we're basically responsible for casting to Pathname
File.typed_read(Pathname.new("hello.txt"))
File.typed_read(Pathname.new(ATadContrivedSure.new.to_s))

You know what this reminds me of? Java 1.

BufferedReader reader = new BufferedReader(new FileReader("hello.txt"));
// 👆specifically this line 🤢

String         line = null;
StringBuilder  stringBuilder = new StringBuilder();

try {
  while ((line = reader.readLine()) != null) {
    stringBuilder.append(line);
    stringBuilder.append(System.getProperty("line.separator"));
  }

  return stringBuilder.toString();
}
finally {
  reader.close();
}

And I hear you asking, “Well, wouldn’t you just let typed_read take an Object?”. Yeah, and I’m sure that’s how File#read will be type-annotated – something like:

class File
  def read: (pathname: Object) -> String
end

And maybe we’ll all write typed code that’s equally as flexible as what we have today. But I’m skeptical. I worry types will enforce a certain rigidity in your design. After all, Java also has a top level Object class everything else inherits from, look what they came up with.

Static typing is powerful, and I’m not saying you’re wrong to love and prefer it, but dynamic typing also has its place. Ruby has always been one of those places – a language and community built around the advantages of dynamic typing. I’d hate to lose that.

So don’t change Ruby. 2


  1. Yes, this isn’t the post-Java-7 way of doing things anymore, but it was when I was writing Java. 

  2. For context: there’s been a lot of excitement around Ruby 3 having types. I’m less excited.