4 ms·
> While Go does support passing around functions (and this is occasionally useful), it doesn’t really support the use of functions to build up composable abstra
by cdoxsey 8y ago
> While Go does support passing around functions (and this is occasionally useful), it doesn’t really support the use of functions to build up composable abstractions that feel like extensions to the language. It favours the concrete, inline expression of logic, with function calls acting primarily as a mechanism for structured programming.
I see what the Author is getting at, but he's missing the primary mechanism Go uses for this: Interfaces. This is hard to explain in the abstract, so here's an example I gave in a talk once: (bit.ly/1mBisa6)
Suppose we are building a new podcast platform which dynamically splices advertisements into mp3s. We will do this with an HTTP server. In Go you do:
f, err := os.Open("somefile")
...
http.ServeContent(w, r, "", time.Now(), f)
ServeContent takes an io.ReadSeeker. Now we build a Splice function:
func Splice(
src io.ReadSeeker,
splice map[time.Duration]io.ReadSeeker,
) (io.ReadSeeker, error)
Used like this:
f1, _ := os.Open("/tmp/1.mp3")
f2, _ := os.Open("/tmp/2.mp3")
f3, _ := os.Open("/tmp/3.mp3")
final, _ := mp3.Splice(f1, map[time.Duration]io.ReadSeeker{
time.Second * 5: f2,
time.Second * 10: f3,
})
Splice uses two additional functions. First the MultiReadSeeker, which simply concatenates ReadSeekers:
func NewMultiReadSeeker(
readSeekers ...io.ReadSeeker,
) io.ReadSeeker
Next the SectionReadSeeker, which gives you a section of a ReadSeeker:
func NewSectionReadSeeker(
src io.ReadSeeker,
offset, length int64,
) io.ReadSeeker
So it ends up being:
final := NewMultiReadSeeker(
NewSectionReadSeeker(f1, 0, A),
f2,
NewSectionReadSeeker(f1, A, B-A),
f3,
NewSectionReadSeeker(f1, B, C-B),
)
So it's pretty straightforward. There are some really powerful ramifications from these abstractions:
1. The file is never built in its totality in memory. Rather we are representing the operations in a lazy fashion that only get executed when you use them.
2. The ReadSeeker supports seeking, which means we can resume from the browser, or seek to the end and skip the middle bits. With the go HTTP library we basically get this for free.
3. Lots of things use these interfaces. For example if I want to write this to disk, I can just io.Copy it. Or suppose I want to use this as another source for a second splicing. It's ReadSeekers all the way down, so I can do that with no changes. With some tinkering I'm pretty sure you could hook this up to S3 too.