index rss mastodon twitter github linkedin email
Álvaro Ramírez
sponsor

Álvaro Ramírez

18 June 2023 noweb: the lesser known org babel glue

While Org babel's noweb isn't something I've frequently used for literate programming, its simplicity makes it rather versatile to glue all sorts of babel things I hadn't previously considered.

The idea is simple. Add a placeholder like <<other-block>> to an org babel source block, and it will be automatically replaced (verbatim) with the content (or result) of referred block before execution. You'll also need the :noweb yes header argument.

#+NAME: other-block
#+begin_src swift
  print("Hello 0")
#+end_src

#+RESULTS: other-block
: Hello 0

#+BEGIN_SRC swift :noweb yes
  <<other-block>>
  print("Hello 1")
#+END_SRC

#+RESULTS:
: Hello 0
: Hello 1

Since <<other-block>> is replaced with the content of said block, at execution time, the block is effectively equivalent to executing:

print("Hello 0")
print("Hello 1")
Hello 0
Hello 1

Why is this so versatile? Org babel can include/execute all sorts of languages, so you can mix and match the result from one language and massage it to appear as the body of another block using the same (or different) language.

I was recently asked how to include the result from one babel block in another using ob-chatgpt-shell. While the initial question was looking for a solution involving variables, we can use noweb to achieve a similar goal.

Note that in this case, I'll be using <<hello()>>, with (), to refer to #+RESULTS: rather than the source block itself.

#+NAME: hello
#+BEGIN_SRC chatgpt-shell
Say hello in spanish
#+END_SRC

#+RESULTS: hello
Hola

#+BEGIN_SRC chatgpt-shell :noweb yes
<<hello()>>
What does the previous line say verbatim?
#+END_SRC

Executing the block

<<hello()>>
What does the previous line say verbatim?

Gives us

The previous line says "Hola".

On a similar note, I was asked if the results from a previous source block could be fed to a Swift Chart block using ob-swiftui.

While I'm new to Swift Charts, I do love glueing things via Emacs lisp. I figured I could write a little elisp to generate random data and feed it to a SwiftUI block via <<data()>>. The result is pretty neat, based on Apple's LineMark example.

data-chart.gif

#+NAME: data
#+begin_src emacs-lisp :lexical no
  (concat (mapconcat (lambda (n)
                       (format "MonthlyHoursOfSunshine(city: \"Seattle\", month: %d, hoursOfSunshine: %d),"
                               n (random 100)))
                     (number-sequence 1 20) "\n")
          "\n"
          (mapconcat (lambda (n)
                       (format "MonthlyHoursOfSunshine(city: \"Cupertino\", month: %d, hoursOfSunshine: %d),"
                               n (random 100)))
                     (number-sequence 1 20) "\n"))
#+end_src

#+begin_src swiftui :results file :noweb yes
  import Charts

  struct MonthlyHoursOfSunshine: Identifiable {
    var city: String
    var date: Date
    var hoursOfSunshine: Double
    var id = UUID()

    init(city: String, month: Int, hoursOfSunshine: Double) {
      let calendar = Calendar.autoupdatingCurrent
      self.city = city
      self.date = calendar.date(from: DateComponents(year: 2020, month: month))!
      self.hoursOfSunshine = hoursOfSunshine
    }
  }

  struct ContentView: View {
    var data: [MonthlyHoursOfSunshine] = [
<<data()>>
    ]
    var body: some View {
      Chart(data) {
        LineMark(
          x: .value("Month", $0.date),
          y: .value("Hours of Sunshine", $0.hoursOfSunshine)
        )
        .foregroundStyle(by: .value("City", $0.city))
      }
      .frame(minWidth: 800, minHeight: 300)
      .padding()
      .colorScheme(.dark)
    }
  }
#+end_src

While I've shown fairly basic usages of noweb, we can accomplish some nifty integrations. Check out the noweb reference syntax for more examples and additional header arguments like tangle, strip-tangle, and others.