liveBook · Manning

Coupling

As explained in Functional Design and Architecture: Examples in Haskell

Decoupling is the reduction of dependencies between software components or language modules so they can change more independently.

Coupling is the degree to which a component depends on outside systems, modules, or implementation details. Low coupling reduces those dependencies so responsibilities stay focused, implementations can vary, and changes are less likely to spread through unrelated code. It works with high cohesion: each part should do one clear job rather than absorb device quirks, data correction, transport, and business logic at once. section 1.3.1 section 1.6

A warning sign is code that directly calls a native thermometer, knows its temperature representation, applies a device-specific correction, and sends to a server. That observer is coupled to concrete devices and transport, making it fragile, unpredictable, and hard to test. The better question is not merely how to split code, but which knowledge belongs behind a boundary. section 1.3.1

Listing 1.2 Loosely coupled object-oriented code
trait ISensor {                                          #1
  def getData() : Float
  def getName() : String
  def getDataType() : String
}
  
trait IConnection {                                      #2
  def send(dataType: String, name: String, data: Float)
}
  
final class Observer (val sensor: ISensor,               #3
                      val connection: IConnection) {
  def readAndSendData() {
    val data = sensor.getData()                          #4
    val sensorName = sensor.getName()
    val dataType = sensor.getDataType()
    connection.send(dataType, sensorName, data)
}}

Compare the observer’s narrow dependencies on a sensor and connection interface with direct knowledge of native and server APIs. section 1.3.1

Common ways to reduce coupling include:

  • Depend on a small interface rather than a concrete sensor, server, or subsystem implementation; inject the required collaborators into the client. section 1.3.1 section 4.2.2
  • Keep implementation layers independent of vendor assets. Treat concrete vendors and device descriptions as configuration data rather than knowledge embedded in general domain code. section 4.1.1
  • Place interaction logic behind an interop bus when a control component would otherwise coordinate many components directly. section 3.2.1
  • Expose stable top-level modules that reexport a subsystem while hiding its internal organization. section 3.2.4
  • Parameterize algebraic data types when nested eDSLs should remain usable without knowing the language that contains them. section 5.2.4

Replacing vendor knowledge with a boundary

A device-building module becomes wrongly coupled when its internal functions import a specific vendor such as AAA to select handlers. Adding another vendor then invites edits to general implementation code. Separate the scopes instead: keep general hardware interfaces independent, let assets depend on vendor modules, and connect them through the appropriate layer. The implementation then addresses general domain behavior while vendor-specific details remain data or assets. section 4.1.1

Coupling is not limited to object-oriented code. Functional code can tightly bind itself to native I/O and remote services too. Functional designs address that pressure through explicit handling of side effects, interfaces or handles built from functions, modular boundaries, and parameterized types; the aim remains independently changeable parts rather than a universal preference for one paradigm. section 1.3.1 section 4.2.2

How Functional Design and Architecture: Examples in Haskell covers it

How deep it goes
Explains coupling through refactorings, module-layer dependencies, service handles, and parameterized ADTs rather than formal metrics.
Examples
Contrasts a temperature observer, vendor-dependent device construction, Service Handle values, and parameterized Hdl and LogicControl languages.
What you can do after
Apply it via interfaces, handles, module boundaries, and dependency direction to make implementations replaceable and testable.
How it connects
Connects coupling with cohesion, interfaces, dependency injection, side effects, modularity, SRP, facades, and algebraic data types.

Also explained in

Compare how 17 books explain coupling →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Functional Design and Architecture: Examples in Haskell. The section links point to the parts of the book it draws on.