|
The problem with that is that it forces moving the code above where it is passed, which can greatly clutter things. For example, consider the function "forever" in Haskell: forever action = do
action
forever action
Now you can use it as a new control structure, e.g: forever $ do
(sock, addr) <- accept listener
forkIO $ handleClient sock
The $ simply means "apply" and is low-precedence, so it removes the need to put () around the entire argument to be applied.In Python, you could define: def forever(action):
action()
forever(action)
but then, to use it, you have to give a name to your function, so: def accept_once():
(sock, addr) = listener.accept()
fork(partial(handleClient, sock))
forever(accept_once)
This makes "forever" much less useful as a new control structure/looping primitive.In this sense, Python makes DSLs less usable. The built-in primitives are first-class, and can have code directly within their use. Library functions are second-class, and can only have code passed by name which must then fully appear before the use. Another example is callbacks. The reason "twisted" is probably called "twisted", and that people hate callbacks so much, is that it forces writing the code in backwards order, precisely because of this problem. For example: def handle_result(result):
print "Done:", result
def connection_started(conn):
conn.request(SomeRequest(), handle_result)
def start():
start_connecting(connection_started)
Compare this with Haskell, as an example: start = startConnecting $ \conn -> do
request conn SomeRequest $ \result -> do
putStrLn $ "Done: " ++ show result
Note, in Haskell, this would actually be worked out to be (by overloading the semicolon): start = do
conn <- startConnecting
result <- request conn SomeRequest
putStrLn $ "Done: " ++ show result
But even the former nested representation is better than the backwards ("twisted") representation that makes people hate callbacks so much. |