It's less verbose, increases flexibility, and decreases coupling. What's not to like?
Let's compare the use of a closure for handling an event versus using a class. The class must implement a specific interface. This means the event handler must not only have specific arguments, but also a specific name. If you want that same object to be able to handle events from other sources, those sources either cannot use the same function name in the handler interface, or the event source must provide some other means of distinguishing the events.
That leads to messes like Java having an ActionListener, AdjustmentListener, AncestorListener, AWTEventListener, BeanContextMembershipListener, BeanContextServiceRevokedListener, BeanContextServices, BeanContextServicesListener, CaretListener, CellEditorListener, ChangeListener, ComponentListener, ConnectionEventListener, ContainerListener, ControllerEventListener, DocumentListener, DragGestureListener, DragSourceListener, DragSourceMotionListener, DropTargetListener, FlavorListener, FocusListener, HandshakeCompletedListener, HierarchyBoundsListener, HierarchyListener, HyperlinkListener, IIOReadProgressListener, IIOReadUpdateListener, IIOReadWarningListener, IIOWriteProgressListener, IIOWriteWarningListener, InputMethodListener, InternalFrameListener, ItemListener, KeyListener, LineListener, ListDataListener, ListSelectionListener, MenuDragMouseListener, MenuKeyListener, MenuListener, MetaEventListener, MouseInputListener, MouseListener, MouseMotionListener, MouseWheelListener, NamespaceChangeListener, NamingListener, NodeChangeListener, NotificationListener, ObjectChangeListener, PopupMenuListener, PreferenceChangeListener, PropertyChangeListener, RowSetListener, RowSorterListener, SSLSessionBindingListener, StatementEventListener, TableColumnModelListener, TableModelListener, TextListener, TreeExpansionListener, TreeModelListener, TreeSelectionListener, TreeWillExpandListener, UndoableEditListener, UnsolicitedNotificationListener, VetoableChangeListener, WindowFocusListener, WindowListener, and WindowStateListener.
Using closures removes the need to create all those different unique interfaces and worry about conflicts between them. This simplifies the library design, but also makes it easier for you as a consumer of that library. You don't have to remember or look up which interface to implement. You don't have to remember or look up whether you need to name your handler function actionPerformed
or stateChanged
. You don't have to create empty implementations of the parts of the interface you don't care about.
You just have to create a closure that takes the appropriate arguments, which are usually easy to make consistent throughout the system, and much of the time you don't even need those arguments because of the closure's context. It's a much smaller mental load.
Edit: I was not aware of C# delegates when I wrote the first part of my answer. They are unique to .Net (which I don't use), and most other libraries use full closures in the same situations as delegates. Languages that don't have closures usually implement an interface for each event type, which creates the naming problems I mentioned. Delegates solve that problem by enabling you to name your event handler whatever you want.
That being said, closures are generally a much more concise way to solve the same problem. They don't require the programmer to explicitly put their state into a class. They don't require their own names. They are encapsulated within the function where they are defined, and therefore very limited in scope, which makes it very easy to create as many as you need without worrying about conflicting with each other.
Just don't forget about principles like DRY. If you find yourself copying and pasting a closure, you should probably refactor it out so it is shareable and has a useful name.