Consider the following example:
The user wants a program to calculate a few fibonacci numbers. Sounds easy enough. pseudocode:
stdout.write("How many fibonacci numbers do you want to calculate? ")
int count = int(stdin.readline())
while count>0:
stdout.writeline(calculate_next_fibonacci_number())
count--
Even a very simple program like this is already flawed:
- The program writes to stdout. That is not actually what the programmer intended, is it? The intention is to display a bunch of number to the user, not to write some text to stdout - there's no guarantee the user will ever see the text that's written to stdout.
- Similarly, the user input is read from stdin, which is a text (or file, if you prefer) interface, when in reality, the program requires a number, not text. This seems fundamentally wrong.
Let's think about what we're trying to do more abstractly. We want to:
- Calculate a bunch of numbers. How many is up to the user.
- display the results to the user.
Why, then, don't we write code exactly like that? (Most) programming languages provide this thing called a "function", which accepts parameters and uses them to do something. Does that not sound exactly like what we're trying to do?
void display_fibonacci_numbers(
int number "how many fibonacci numbers to calculate"):
Sequence<int> numbers
while number>0:
numbers.append(calculate_next_fibonacci_number())
number--
notify(numbers)
display_fibonacci_numbers()
This code is, of course, incomplete - the notify
function isn't implemented anywhere, and the user needs some way of inputting a number. I would imagine the user's operating system or desktop manager or whatever to take care of that - it could display a terminal, it could generate a GUI, it could tell the user do write the number into the air with his nose; either way it does not (should not) concern me, the programmer.
It is my belief that software should be more abstract.
A few more examples.
- inter-process communication. How do you do it? Sockets? Signals? DBus? Files? The goal isn't to use sockets, signals or dbus, it's to communicate with another process. Why not something like
Process.from_name("Music player").play_random_song()
? - Downloading files.
local_file.write(remote_file.read())
? Why notdownload(url)
, which could, depending on the system configuration, start the download right away, but with a low priority so as not to slow down other downloads, or add it to the download queue to be downloaded later, or whatever? - File paths. Why on earth are file paths still strings (in most languages, at least)? Why do we have to deal with whether the path separator is
/
or\
, whether it's.
,..
, or whatever? Why is there noFilePath
class that takes care of this?
Going a step further, why not apply the duck-typing concept to modules? For example, in python, HTML is often parsed using the beautifulsoup
module:
from bs4 import BeautifulSoup
page= BeautifulSoup(urlopen('http://test.at'))
Again, the programmer's goal wasn't to use BeautifulSoup, but to parse HTML. Why, then, would he explicitly tell his code to use the BeautifulSoup module? What he really wants is to
import HTMLParser
page= HTMLParser.parse(urlopen('http://test.at'))
Duck-typing for modules: Who cares what module it is as long as it does what I want?
Why is programming not like this? Am I overlooking something, something that makes all this impossible (or impractical)?
requests
because its API differs fromurllib
. If it stuck to the API, it would have no benefits (and the downsides of being less mature and battle-hardened). – Dec 09 '14 at 13:50We can't have built-in methods for everything though, at some point you're going to have to implement something yourself, at which point you're no longer programming "in the abstract on a grander scale".
– Ajedi32 Dec 09 '14 at 14:40download(url)
and real path objects. – Telastyn Dec 09 '14 at 14:44Interface
s are not a real solution because you have to declare them up front, they don't support binary methods that look at the private fields of both arguments (e.g. merging collections, custom number types), and they're more open than necessary (you just want to work with one implementation). I wonder how many redundant Point2D and Tuple classes there are in github? – Doval Dec 10 '14 at 14:32