For my Operating Systems class, I needed a linked list data structure and didn't have one already written. During the course of thinking about how to implement a linked list again (it had been a while..), I decided that a lot of the most common functions used on linked lists can be implemented based on applying or composing a few core functions.
First, I wrote a function that iterates over the list starting from list->head while applying a function to each element. After each iteration, if the function returned false, then stop iterating and return a pointer to the node which terminated the loop. Otherwise, iterate until list->tail had the function applied to it.
Basically all of the rest of my functions fall from this one function. I was able to quickly implement map by calling the iteration function and always returning true (and therefore the iteration will never end prematurely). I was also able to implement a general iterative "test" function, which requires a function parameter that accepts two node arguments and produces a boolean. The point of the iterative test function is to implement things like sorting or testing for equality in a composable way.
I think my attempt isn't half bad, given that I was writing in C without any anonymous functions or closures (of which my knowledge is limited). That being said, I'm glad to hear what criticisms the community has on the implementation on a whole or nit-picking.
llist.h
#ifndef LLIST_H
#define LLIST_H
/* Fields */
typedef struct _node {
  struct _node *next, *prev;
  void *data;
} node;
typedef struct {
  node *head, *tail;
  size_t size;
} llist;
typedef enum {FALSE, TRUE} bool;
typedef void (*nodefunc)(node *);
typedef bool (*nodeiter)(node *);
typedef bool (*nodetest)(void *, void *);
/* Methods */
void llist_init(llist *l);
void llist_node_init(node *n);
void map(llist *l, nodefunc f);
void *llist_iter(llist *l, nodeiter f);
void *llist_test(llist *l, void *q, nodetest t);
void llist_append(llist *l, node *n);
void llist_remove(llist *l, node *n);
void llist_destroy(llist *l, nodefunc node_free);
#endif
llist.c
#include <stdlib.h>
#include "llist.h"
void llist_init(llist *l) {
  l->head = NULL;
  l->tail = NULL;
  l->size = 0;
}
void llist_node_init(node *n) {
  n->data = NULL;
  n->next = NULL;
  n->prev = NULL;
}
void *llist_iter(llist *l, nodeiter f) {
  node *n = l->head, *next = NULL;
  void *result = 0;
  while (n != NULL && result == 0) {
    next = n->next;
    if (!f(n))
      result = (void*)n;
    n = next;
  }
  return result;
}
void *llist_test(llist *l, void *q, nodetest t) {
  bool iter_test(node *n) {
    return !( t((void*)n->data, q) );
  }
  return llist_iter(l, iter_test);
}
// not a true map since f can have side effects
void map(llist *l, nodefunc f) {
  bool iter_true(node *n) {
    f(n);
    return TRUE;
  }
  llist_iter(l, iter_true);
}
void llist_append(llist *l, node *n) {
  if (l->size == 0) {
    l->head = n;
    l->tail = n;
  } else {
    n->prev = l->tail;
    l->tail->next = n;
    l->tail = n;
  }
  l->size++;
}
void llist_remove(llist *l, node *n) {
  if (n) {
    if (n->prev)
      n->prev->next = n->next;
    else
      l->head = n->next;
    if (n->next)
      n->next->prev = n->prev;
    else
      l->tail = n->prev;
    l->size--;
  }
}
void llist_destroy(llist *l, nodefunc node_free) {
  void iter_remove(node *n) {
    llist_remove(l, n);
    node_free(n);
  }
  map(l, iter_remove);
}
From this, I'm able to implement in a program, for example, a print function based on the type of data that is stored in the linked list:
llist_test.c
#include <stdio.h>
#include <stdlib.h>
#include "llist.h"
void llist_print(llist *l) {
  void iter_print(node *n) {
    printf("value: %d\n", *(int*)n->data);
  }
  map(l, iter_print);
}
void node_free(node *n) { free(n); }
node *alloc_node(int *data) {
  node *n = malloc(sizeof(node));
  llist_node_init(n);
  n->data = data;
  return n;
}
int main() {
  llist *l = malloc(sizeof(llist));
  llist_init(l);
  int x = 4, y = 8;
  llist_append(l, alloc_node(&x));
  // ...
  llist_destroy(l, node_free);
  free(l);
  return 0;
}
