Struct Embedding in Clojure

Clojure supports the concept of protocols and records, which can be used to achieve composition similar to struct embedding in other languages. This example demonstrates how to use these features to create a similar structure and behavior.

(ns struct-embedding
  (:require [clojure.string :as str]))

(defprotocol Describer
  (describe [this]))

(defrecord Base [num]
  Describer
  (describe [this]
    (str "base with num=" (:num this))))

(defrecord Container [base str]
  Describer
  (describe [this]
    (describe (:base this))))

(defn -main []
  (let [co (->Container (->Base 1) "some name")]
    ; We can access the base's fields directly on co
    (println (str "co={num: " (-> co :base :num) ", str: " (:str co) "}"))

    ; Alternatively, we can spell out the full path using the embedded type name
    (println "also num:" (-> co :base :num))

    ; Since Container includes Base, we can call describe on Container
    (println "describe:" (describe co))

    ; We can use the Describer protocol to demonstrate interface-like behavior
    (let [d co]
      (println "describer:" (describe d)))))

(-main)

When creating records with literals, we initialize the embedding explicitly. In this case, we create a Base instance and pass it to the Container constructor.

The Container record includes a Base instance, allowing us to access its fields through the Container. We can also directly invoke methods (in this case, the describe function) that were defined for Base on a Container instance.

The Describer protocol is used to define a common interface, which both Base and Container implement. This demonstrates how Clojure can achieve behavior similar to interface implementation through embedding.

To run this program, save it as struct_embedding.clj and use:

$ clojure struct_embedding.clj
co={num: 1, str: some name}
also num: 1
describe: base with num=1
describer: base with num=1

This example showcases how Clojure can use records and protocols to achieve composition and interface-like behavior, similar to struct embedding in other languages.