diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,7 @@
+Revision history for haskell-igraph
+===================================
+
+v0.4.0 -- 2018-04-20
+-------------------
+
+* A new attribute interface written in C. The graph attributes are now directly serialized into bytestring using "cereal" (before we used the `Show` instance).
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
diff --git a/cbits/bytestring.c b/cbits/bytestring.c
new file mode 100644
--- /dev/null
+++ b/cbits/bytestring.c
@@ -0,0 +1,359 @@
+#include "bytestring.h"
+#include <assert.h>
+#include <string.h>
+
+int bsvector_init(bsvector_t *sv, long int len) {
+  long int i;
+  sv->data=igraph_Calloc(len, bytestring_t*);
+  if (sv->data==0) {
+    IGRAPH_ERROR("bsvector init failed", IGRAPH_ENOMEM);
+  }
+  for (i=0; i<len; i++) {
+    sv->data[i]=new_bytestring(0);
+    if (sv->data[i]==0) {
+      bsvector_destroy(sv);
+      IGRAPH_ERROR("bsvector init failed", IGRAPH_ENOMEM);
+    }
+  }
+  sv->len=len;
+
+  return 0;
+}
+
+void bsvector_destroy(bsvector_t *sv) {
+  long int i;
+  assert(sv != 0);
+  if (sv->data != 0) {
+    for (i=0; i<sv->len; i++) {
+      if (sv->data[i] != 0) {
+        destroy_bytestring(sv->data[i]);
+      }
+    }
+    igraph_Free(sv->data);
+  }
+}
+
+void bsvector_get(const bsvector_t *sv, long int idx, bytestring_t **value) {
+  assert(sv != 0);
+  assert(sv->data != 0);
+  assert(sv->data[idx] != 0);
+  *value = sv->data[idx];
+}
+
+int bsvector_set(bsvector_t *sv, long int idx, const bytestring_t *value) {
+  assert(sv != 0);
+  assert(sv->data != 0);
+
+  if (sv->data[idx] != 0) {
+    destroy_bytestring(sv->data[idx]);
+  }
+  sv->data[idx] = new_bytestring(value->len);
+
+  memcpy(sv->data[idx]->value, value->value, value->len * sizeof(char));
+
+  return 0;
+}
+
+void bsvector_remove_section(bsvector_t *v, long int from, long int to) {
+  long int i;
+
+  assert(v != 0);
+  assert(v->data != 0);
+
+  for (i=from; i<to; i++) {
+    if (v->data[i] != 0) {
+      destroy_bytestring(v->data[i]);
+    }
+  }
+  for (i=0; i<v->len-to; i++) {
+    v->data[from+i]=v->data[to+i];
+  }
+
+  v->len -= (to-from);
+}
+
+void bsvector_remove(bsvector_t *v, long int elem) {
+  assert(v != 0);
+  assert(v->data != 0);
+  bsvector_remove_section(v, elem, elem+1);
+}
+
+/*
+void bsvector_move_interval(bsvector_t *v, long int begin,
+				   long int end, long int to) {
+  long int i;
+  assert(v != 0);
+  assert(v->data != 0);
+  for (i=to; i<to+end-begin; i++) {
+    if (v->data[i] != 0) {
+      destroy_bytestring(v->data[i]);
+    }
+  }
+  for (i=0; i<end-begin; i++) {
+    if (v->data[begin+i] != 0) {
+      size_t len=strlen(v->data[begin+i])+1;
+      v->data[to+i]=igraph_Calloc(len, char);
+      memcpy(v->data[to+i], v->data[begin+i], sizeof(char)*len);
+    }
+  }
+}
+*/
+
+int bsvector_copy(bsvector_t *to, const bsvector_t *from) {
+  long int i;
+  bytestring_t *str;
+  assert(from != 0);
+/*   assert(from->data != 0); */
+  to->data=igraph_Calloc(from->len, bytestring_t*);
+  if (to->data==0) {
+    IGRAPH_ERROR("Cannot copy string vector", IGRAPH_ENOMEM);
+  }
+  to->len=from->len;
+
+  for (i=0; i<from->len; i++) {
+    int ret;
+    bsvector_get(from, i, &str);
+    ret=bsvector_set(to, i, str);
+    if (ret != 0) {
+      bsvector_destroy(to);
+      IGRAPH_ERROR("cannot copy string vector", ret);
+    }
+  }
+
+  return 0;
+}
+
+int bsvector_append(bsvector_t *to, const bsvector_t *from) {
+  long int len1=bsvector_size(to), len2=bsvector_size(from);
+  long int i;
+  igraph_bool_t error=0;
+  IGRAPH_CHECK(bsvector_resize(to, len1+len2));
+  for (i=0; i<len2; i++) {
+    if (from->data[i]->len > 0) {
+      bsvector_set(to, len1+i, from->data[i]);
+      if (!to->data[len1+i]) {
+        error=1;
+        break;
+      }
+    }
+  }
+  if (error) {
+    bsvector_resize(to, len1);
+    IGRAPH_ERROR("Cannot append string vector", IGRAPH_ENOMEM);
+  }
+  return 0;
+}
+
+void bsvector_clear(bsvector_t *sv) {
+  long int i, n=bsvector_size(sv);
+  bytestring_t **tmp;
+
+  for (i=0; i<n; i++) {
+    destroy_bytestring(sv->data[i]);
+  }
+  sv->len=0;
+  /* try to give back some memory */
+  tmp=igraph_Realloc(sv->data, 1, bytestring_t*);
+  if (tmp != 0) {
+    sv->data=tmp;
+  }
+}
+
+int bsvector_resize(bsvector_t* v, long int newsize) {
+  long int toadd = newsize - v->len, i, j;
+  bytestring_t **tmp;
+  long int reallocsize=newsize;
+  if (reallocsize==0) { reallocsize=1; }
+
+  assert(v != 0);
+  assert(v->data != 0);
+  if (newsize < v->len) {
+    for (i=newsize; i<v->len; i++) {
+      destroy_bytestring(v->data[i]);
+    }
+    /* try to give back some space */
+    tmp=igraph_Realloc(v->data, (size_t) reallocsize, bytestring_t*);
+    if (tmp != 0) {
+      v->data=tmp;
+    }
+  } else if (newsize > v->len) {
+    igraph_bool_t error=0;
+    tmp=igraph_Realloc(v->data, (size_t) reallocsize, bytestring_t*);
+    if (tmp==0) {
+      IGRAPH_ERROR("cannot resize string vector", IGRAPH_ENOMEM);
+    }
+    v->data = tmp;
+
+    for (i=0; i<toadd; i++) {
+      v->data[v->len+i] = new_bytestring(0);
+      if (v->data[v->len+i] == 0) {
+	       error=1;
+	       break;
+      }
+    }
+    if (error) {
+      /* There was an error, free everything we've allocated so far */
+      for (j=0; j<i; j++) {
+	       if (v->data[v->len+i] != 0) {
+	          destroy_bytestring(v->data[v->len+i]);
+	       }
+      }
+      /* Try to give back space */
+      tmp=igraph_Realloc(v->data, (size_t) (v->len), bytestring_t*);
+      if (tmp != 0) {
+	       v->data=tmp;
+      }
+      IGRAPH_ERROR("Cannot resize string vector", IGRAPH_ENOMEM);
+    }
+  }
+  v->len = newsize;
+
+  return 0;
+}
+
+int bsvector_add(bsvector_t *v, const bytestring_t *value) {
+  long int s=bsvector_size(v);
+  bytestring_t **tmp;
+  assert(v != 0);
+  assert(v->data != 0);
+  tmp=igraph_Realloc(v->data, (size_t) s+1, bytestring_t*);
+  if (tmp == 0) {
+    IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM);
+  }
+  v->data=tmp;
+  v->data[s]=new_bytestring(value->len);
+  if (v->data[s]==0) {
+    IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM);
+  }
+  bsvector_set(v, s, value);
+  v->len += 1;
+
+  return 0;
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_permdelete
+ * \brief Removes elements from a string vector (for internal use)
+ */
+
+void bsvector_permdelete(bsvector_t *v, const igraph_vector_t *index,
+				long int nremove) {
+  long int i;
+  bytestring_t **tmp;
+  assert(v != 0);
+  assert(v->data != 0);
+
+  for (i=0; i<bsvector_size(v); i++) {
+    if (VECTOR(*index)[i] != 0) {
+      v->data[ (long int) VECTOR(*index)[i]-1 ] = v->data[i];
+    } else {
+      destroy_bytestring(v->data[i]);
+    }
+  }
+  /* Try to make it shorter */
+  tmp=igraph_Realloc(v->data, v->len-nremove ?
+		     (size_t) (v->len-nremove) : 1, bytestring_t*);
+  if (tmp != 0) {
+    v->data=tmp;
+  }
+  v->len -= nremove;
+}
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_remove_negidx
+ * \brief Removes elements from a string vector (for internal use)
+ */
+
+void bsvector_remove_negidx(bsvector_t *v, const igraph_vector_t *neg,
+				   long int nremove) {
+  long int i, idx=0;
+  bytestring_t **tmp;
+  assert(v != 0);
+  assert(v->data != 0);
+  for (i=0; i<bsvector_size(v); i++) {
+    if (VECTOR(*neg)[i] >= 0) {
+      v->data[idx++] = v->data[i];
+    } else {
+      destroy_bytestring(v->data[i]);
+    }
+  }
+  /* Try to give back some memory */
+  tmp=igraph_Realloc(v->data, v->len-nremove ?
+		     (size_t) (v->len-nremove) : 1, bytestring_t*);
+  if (tmp != 0) {
+    v->data=tmp;
+  }
+  v->len -= nremove;
+}
+
+int bsvector_index(const bsvector_t *v, bsvector_t *newv,
+                   const igraph_vector_t *idx) {
+  long int i, newlen=igraph_vector_size(idx);
+  IGRAPH_CHECK(bsvector_resize(newv, newlen));
+
+  for (i=0; i<newlen; i++) {
+    long int j=(long int) VECTOR(*idx)[i];
+    bytestring_t *str;
+    bsvector_get(v, j, &str);
+    bsvector_set(newv, i, str);
+  }
+
+  return 0;
+}
+
+long int bsvector_size(const bsvector_t *sv) {
+  assert(sv != 0);
+  return sv->len;
+}
+
+bytestring_t* new_bytestring(int n) {
+    bytestring_t *newstr = igraph_Calloc(1, bytestring_t);
+    char *str = igraph_Calloc(n, char);
+    newstr->len = n;
+    newstr->value = str;
+    return newstr;
+}
+
+void destroy_bytestring(bytestring_t* str) {
+  if (str != NULL) {
+    free(str->value);
+    free(str);
+  }
+}
+
+char* bytestring_to_char(bytestring_t* from) {
+  char *str = igraph_Calloc(from->len + sizeof(long int), char);
+  memcpy(str, &from->len, sizeof(long int));
+  memcpy(str + sizeof(long int), from->value, from->len);
+  return str;
+}
+
+bytestring_t* char_to_bytestring(char* from) {
+  unsigned long int *n;
+  memcpy(n, from, sizeof(long int));
+  bytestring_t *str = new_bytestring(*n);
+  memcpy(str->value, from+sizeof(long int), *n);
+  return str;
+}
+
+igraph_strvector_t* bsvector_to_strvector(bsvector_t* from) {
+  igraph_strvector_t *str;
+  size_t i;
+  igraph_strvector_init(str, from->len);
+  for (i = 0; i++; i < from->len) {
+    igraph_strvector_set(str, i, bytestring_to_char(from->data[i]));
+  }
+  return str;
+}
+
+bsvector_t* strvector_to_bsvector(igraph_strvector_t* from) {
+  bsvector_t *str;
+  size_t i;
+  bsvector_init(str, from->len);
+  for (i = 0; i++; i < from->len) {
+    bsvector_set(str, i, char_to_bytestring(from->data[i]));
+  }
+  return str;
+}
diff --git a/cbits/haskell_attributes.c b/cbits/haskell_attributes.c
new file mode 100644
--- /dev/null
+++ b/cbits/haskell_attributes.c
@@ -0,0 +1,1900 @@
+#include "haskell_attributes.h"
+
+/* An attribute is either a numeric vector (vector_t) or a string
+   vector (strvector_t). The attribute itself is stored in a
+   struct igraph_attribute_record_t, there is one such object for each
+   attribute. The igraph_t has a pointer to an array of three
+   vector_ptr_t's which contains pointers to
+   igraph_haskell_attribute_t's. Graph attributes are first, then vertex
+   and edge attributes. */
+
+igraph_bool_t igraph_haskell_attribute_find(const igraph_vector_ptr_t *ptrvec,
+				       const char *name, long int *idx) {
+  long int i, n=igraph_vector_ptr_size(ptrvec);
+  igraph_bool_t l=0;
+  for (i=0; !l && i<n; i++) {
+    igraph_attribute_record_t *rec=VECTOR(*ptrvec)[i];
+    l= !strcmp(rec->name, name);
+  }
+  if (idx) { *idx=i-1; }
+  return l;
+}
+
+int igraph_haskell_attributes_copy_attribute_record(igraph_attribute_record_t **newrec,
+					       const igraph_attribute_record_t *rec) {
+  bsvector_t *str, *newstr;
+
+  *newrec=igraph_Calloc(1, igraph_attribute_record_t);
+  if (!(*newrec)) { IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); }
+  IGRAPH_FINALLY(igraph_free, *newrec);
+  (*newrec)->type=rec->type;
+  (*newrec)->name=strdup(rec->name);
+  if (!(*newrec)->name) { IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); }
+  IGRAPH_FINALLY(igraph_free, (void*)(*newrec)->name);
+
+  if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+    str=(bsvector_t*)rec->value;
+    newstr=igraph_Calloc(1, bsvector_t);
+    if (!newstr) {
+      IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, newstr);
+    IGRAPH_CHECK(bsvector_copy(newstr, str));
+    IGRAPH_FINALLY(bsvector_destroy, newstr);
+    (*newrec)->value=newstr;
+  } else {
+    IGRAPH_ERROR("Wrong attribute type", IGRAPH_ENOMEM);
+  }
+
+  IGRAPH_FINALLY_CLEAN(4);
+  return 0;
+}
+
+
+int igraph_haskell_attribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) {
+  igraph_attribute_record_t *attr_rec;
+  long int i, n;
+  igraph_haskell_attributes_t *nattr;
+
+  n = attr ? igraph_vector_ptr_size(attr) : 0;
+
+  nattr=igraph_Calloc(1, igraph_haskell_attributes_t);
+  if (!nattr) {
+    IGRAPH_ERROR("Can't init attributes", IGRAPH_ENOMEM);
+  }
+  IGRAPH_FINALLY(igraph_free, nattr);
+
+  IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->gal, n));
+  IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->gal);
+  IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->val, 0));
+  IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->val);
+  IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->eal, 0));
+  IGRAPH_FINALLY_CLEAN(3);
+
+  for (i=0; i<n; i++) {
+    IGRAPH_CHECK(igraph_haskell_attributes_copy_attribute_record(
+	  &attr_rec, VECTOR(*attr)[i]));
+    VECTOR(nattr->gal)[i] = attr_rec;
+  }
+
+  graph->attr=nattr;
+
+  return 0;
+}
+
+void igraph_haskell_attribute_destroy(igraph_t *graph) {
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *als[3]= { &attr->gal, &attr->val, &attr->eal };
+  long int i, n, a;
+  bsvector_t *str;
+  igraph_attribute_record_t *rec;
+  for (a=0; a<3; a++) {
+    n=igraph_vector_ptr_size(als[a]);
+    for (i=0; i<n; i++) {
+      rec=VECTOR(*als[a])[i];
+      if (rec) {
+				if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+					str=(bsvector_t*)rec->value;
+					bsvector_destroy(str);
+					igraph_free(str);
+				}
+				igraph_free((char*)rec->name);
+				igraph_free(rec);
+			}
+		}
+  }
+  igraph_vector_ptr_destroy(&attr->gal);
+  igraph_vector_ptr_destroy(&attr->val);
+  igraph_vector_ptr_destroy(&attr->eal);
+  igraph_free(graph->attr);
+  graph->attr=0;
+}
+
+/* Almost the same as destroy, but we might have null pointers */
+
+void igraph_haskell_attribute_copy_free(igraph_haskell_attributes_t *attr) {
+  igraph_vector_ptr_t *als[3] = { &attr->gal, &attr->val, &attr->eal };
+  long int i, n, a;
+  bsvector_t *str;
+  igraph_attribute_record_t *rec;
+  for (a=0; a<3; a++) {
+    n=igraph_vector_ptr_size(als[a]);
+    for (i=0; i<n; i++) {
+      rec=VECTOR(*als[a])[i];
+      if (!rec) { continue; }
+			if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+				str=(bsvector_t*)rec->value;
+				bsvector_destroy(str);
+				igraph_free(str);
+			}
+			igraph_free((char*)rec->name);
+      igraph_free(rec);
+    }
+  }
+}
+
+/* No reference counting here. If you use attributes in C you should
+   know what you're doing. */
+
+int igraph_haskell_attribute_copy(igraph_t *to, const igraph_t *from,
+			     igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) {
+  igraph_haskell_attributes_t *attrfrom=from->attr, *attrto;
+  igraph_vector_ptr_t *alto[3], *alfrom[3]={ &attrfrom->gal, &attrfrom->val,
+					     &attrfrom->eal };
+  long int i, n, a;
+  igraph_bool_t copy[3] = { ga, va, ea };
+  to->attr=attrto=igraph_Calloc(1, igraph_haskell_attributes_t);
+  if (!attrto) {
+    IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM);
+  }
+  IGRAPH_FINALLY(igraph_free, attrto);
+  IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->gal, 0);
+  IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->val, 0);
+  IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->eal, 0);
+  IGRAPH_FINALLY_CLEAN(3);
+  IGRAPH_FINALLY(igraph_haskell_attribute_copy_free, attrto);
+
+  alto[0]=&attrto->gal; alto[1]=&attrto->val; alto[2]=&attrto->eal;
+  for (a=0; a<3; a++) {
+    if (copy[a]) {
+      n=igraph_vector_ptr_size(alfrom[a]);
+      IGRAPH_CHECK(igraph_vector_ptr_resize(alto[a], n));
+      igraph_vector_ptr_null(alto[a]);
+      for (i=0; i<n; i++) {
+	igraph_attribute_record_t *newrec;
+	IGRAPH_CHECK(igraph_haskell_attributes_copy_attribute_record(&newrec,
+								VECTOR(*alfrom[a])[i]));
+	VECTOR(*alto[a])[i]=newrec;
+      }
+    }
+  }
+
+  IGRAPH_FINALLY_CLEAN(2);
+  return 0;
+}
+
+int igraph_haskell_attribute_add_vertices(igraph_t *graph, long int nv,
+				     igraph_vector_ptr_t *nattr) {
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *val=&attr->val;
+  long int length=igraph_vector_ptr_size(val);
+  long int nattrno=nattr==NULL ? 0 : igraph_vector_ptr_size(nattr);
+  long int origlen=igraph_vcount(graph)-nv;
+  long int newattrs=0, i;
+  igraph_vector_t news;
+
+  /* First add the new attributes if any */
+  newattrs=0;
+  IGRAPH_VECTOR_INIT_FINALLY(&news, 0);
+  for (i=0; i<nattrno; i++) {
+    igraph_attribute_record_t *nattr_entry=VECTOR(*nattr)[i];
+    const char *nname=nattr_entry->name;
+    long int j;
+    igraph_bool_t l=igraph_haskell_attribute_find(val, nname, &j);
+    if (!l) {
+      newattrs++;
+      IGRAPH_CHECK(igraph_vector_push_back(&news, i));
+    } else {
+      /* check types */
+      if (nattr_entry->type != ((igraph_attribute_record_t*)VECTOR(*val)[j])->type) {
+				IGRAPH_ERROR("You cannot mix attribute types", IGRAPH_EINVAL);
+			}
+    }
+  }
+
+  /* Add NA/empty string vectors for the existing vertices */
+  if (newattrs != 0) {
+    for (i=0; i<newattrs; i++) {
+      igraph_attribute_record_t *tmp=VECTOR(*nattr)[(long int)VECTOR(news)[i]];
+      igraph_attribute_record_t *newrec=igraph_Calloc(1, igraph_attribute_record_t);
+      igraph_attribute_type_t type=tmp->type;
+      if (!newrec) {
+				IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+      }
+      IGRAPH_FINALLY(igraph_free, newrec);
+      newrec->type=type;
+      newrec->name=strdup(tmp->name);
+      if (!newrec->name) {
+				IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+      }
+      IGRAPH_FINALLY(igraph_free, (char*)newrec->name);
+			if (type==IGRAPH_ATTRIBUTE_STRING) {
+				bsvector_t *newstr=igraph_Calloc(1, bsvector_t);
+				if (!newstr) {
+					IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+				}
+				IGRAPH_FINALLY(igraph_free, newstr);
+				BSVECTOR_INIT_FINALLY(newstr, origlen);
+				newrec->value=newstr;
+      }
+      IGRAPH_CHECK(igraph_vector_ptr_push_back(val, newrec));
+      IGRAPH_FINALLY_CLEAN(4);
+    }
+    length=igraph_vector_ptr_size(val);
+  }
+
+
+  /* Now append the new values */
+  for (i=0; i<length; i++) {
+    igraph_attribute_record_t *oldrec=VECTOR(*val)[i];
+    igraph_attribute_record_t *newrec=0;
+    const char *name=oldrec->name;
+    long int j;
+    igraph_bool_t l=0;
+    if (nattr) { l=igraph_haskell_attribute_find(nattr, name, &j); }
+    if (l) {
+      /* This attribute is present in nattr */
+      bsvector_t *oldstr, *newstr;
+      newrec=VECTOR(*nattr)[j];
+      oldstr=(bsvector_t*)oldrec->value;
+      newstr=(bsvector_t*)newrec->value;
+      if (oldrec->type != newrec->type) {
+				IGRAPH_ERROR("Attribute types do not match", IGRAPH_EINVAL);
+      }
+      if (oldrec->type == IGRAPH_ATTRIBUTE_STRING) {
+				if (nv != bsvector_size(newstr)) {
+					IGRAPH_ERROR("Invalid string attribute length", IGRAPH_EINVAL);
+				}
+				IGRAPH_CHECK(bsvector_append(oldstr, newstr));
+			} else {
+				IGRAPH_WARNING("Invalid attribute type");
+			}
+		} else {
+			/* No such attribute, append NA's */
+			bsvector_t *oldstr=(bsvector_t*)oldrec->value;
+			if (oldrec->type == IGRAPH_ATTRIBUTE_STRING) {
+				IGRAPH_CHECK(bsvector_resize(oldstr, origlen+nv));
+			} else {
+				IGRAPH_WARNING("Invalid attribute type");
+			}
+		}
+	}
+
+  igraph_vector_destroy(&news);
+  IGRAPH_FINALLY_CLEAN(1);
+
+  return 0;
+}
+
+void igraph_haskell_attribute_permute_free(igraph_vector_ptr_t *v) {
+  long int i, n=igraph_vector_ptr_size(v);
+  for (i=0; i<n; i++) {
+    igraph_attribute_record_t *rec=VECTOR(*v)[i];
+    igraph_Free(rec->name);
+    if (rec->type == IGRAPH_ATTRIBUTE_STRING) {
+      bsvector_t *strv= (bsvector_t*) rec->value;
+      bsvector_destroy(strv);
+      igraph_Free(strv);
+		}
+    igraph_Free(rec);
+  }
+  igraph_vector_ptr_clear(v);
+}
+
+int igraph_haskell_attribute_permute_vertices(const igraph_t *graph,
+					 igraph_t *newgraph,
+					 const igraph_vector_t *idx) {
+
+  if (graph==newgraph) {
+
+    igraph_haskell_attributes_t *attr=graph->attr;
+    igraph_vector_ptr_t *val=&attr->val;
+    long int valno=igraph_vector_ptr_size(val);
+    long int i;
+
+    for (i=0; i<valno; i++) {
+      igraph_attribute_record_t *oldrec=VECTOR(*val)[i];
+      igraph_attribute_type_t type=oldrec->type;
+      bsvector_t *str, *newstr;
+      if (type == IGRAPH_ATTRIBUTE_STRING) {
+				str=(bsvector_t*)oldrec->value;
+				newstr=igraph_Calloc(1, bsvector_t);
+				if (!newstr) {
+					IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM);
+				}
+				IGRAPH_CHECK(bsvector_init(newstr, 0));
+				IGRAPH_FINALLY(bsvector_destroy, newstr);
+				bsvector_index(str, newstr, idx);
+				oldrec->value=newstr;
+				bsvector_destroy(str);
+				igraph_Free(str);
+				IGRAPH_FINALLY_CLEAN(1);
+			} else {
+				IGRAPH_WARNING("Unknown edge attribute ignored");
+      }
+    }
+	} else {
+    igraph_haskell_attributes_t *attr=graph->attr;
+    igraph_vector_ptr_t *val=&attr->val;
+    long int valno=igraph_vector_ptr_size(val);
+    long int i;
+
+    /* New vertex attributes */
+    igraph_haskell_attributes_t *new_attr=newgraph->attr;
+    igraph_vector_ptr_t *new_val=&new_attr->val;
+    if (igraph_vector_ptr_size(new_val) != 0) {
+      IGRAPH_ERROR("Vertex attributes were already copied",
+		   IGRAPH_EATTRIBUTES);
+    }
+    IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, valno));
+
+    IGRAPH_FINALLY(igraph_haskell_attribute_permute_free, new_val);
+
+    for (i=0; i<valno; i++) {
+      igraph_attribute_record_t *oldrec=VECTOR(*val)[i];
+      igraph_attribute_type_t type=oldrec->type;
+      bsvector_t *str, *newstr;
+
+      /* The record itself */
+      igraph_attribute_record_t *new_rec=
+	igraph_Calloc(1, igraph_attribute_record_t);
+      if (!new_rec) {
+	IGRAPH_ERROR("Cannot create vertex attributes", IGRAPH_ENOMEM);
+      }
+      new_rec->name = strdup(oldrec->name);
+      new_rec->type = oldrec->type;
+      VECTOR(*new_val)[i]=new_rec;
+
+      /* The data */
+      if (type == IGRAPH_ATTRIBUTE_STRING) {
+				str=(bsvector_t*)oldrec->value;
+				newstr=igraph_Calloc(1, bsvector_t);
+				if (!newstr) {
+					IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM);
+				}
+				IGRAPH_CHECK(bsvector_init(newstr, 0));
+				IGRAPH_FINALLY(bsvector_destroy, newstr);
+				bsvector_index(str, newstr, idx);
+				new_rec->value=newstr;
+				IGRAPH_FINALLY_CLEAN(1);
+			} else {
+				IGRAPH_WARNING("Unknown vertex attribute ignored");
+      }
+    }
+  }
+
+  IGRAPH_FINALLY_CLEAN(1);
+  return 0;
+}
+
+int igraph_haskell_attribute_combine_vertices(const igraph_t *graph,
+			 igraph_t *newgraph,
+			 const igraph_vector_ptr_t *merges,
+			 const igraph_attribute_combination_t *comb) {
+	IGRAPH_ERROR("Cannot combine vertex attributes", IGRAPH_ENOMEM);
+  return 1;
+}
+
+int igraph_haskell_attribute_add_edges(igraph_t *graph, const igraph_vector_t *edges,
+				 igraph_vector_ptr_t *nattr) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *eal=&attr->eal;
+  long int ealno=igraph_vector_ptr_size(eal);
+  long int ne=igraph_vector_size(edges)/2;
+  long int origlen=igraph_ecount(graph)-ne;
+  long int nattrno= nattr == 0 ? 0 : igraph_vector_ptr_size(nattr);
+  igraph_vector_t news;
+  long int newattrs, i;
+
+  /* First add the new attributes if any */
+  newattrs=0;
+  IGRAPH_VECTOR_INIT_FINALLY(&news, 0);
+  for (i=0; i<nattrno; i++) {
+    igraph_attribute_record_t *nattr_entry=VECTOR(*nattr)[i];
+    const char *nname=nattr_entry->name;
+    long int j;
+    igraph_bool_t l=igraph_haskell_attribute_find(eal, nname, &j);
+    if (!l) {
+      newattrs++;
+      IGRAPH_CHECK(igraph_vector_push_back(&news, i));
+    } else {
+      /* check types */
+      if (nattr_entry->type != ((igraph_attribute_record_t*)VECTOR(*eal)[j])->type) {
+				IGRAPH_ERROR("You cannot mix attribute types", IGRAPH_EINVAL);
+			}
+		}
+  }
+
+  /* Add NA/empty string vectors for the existing vertices */
+  if (newattrs != 0) {
+    for (i=0; i<newattrs; i++) {
+      igraph_attribute_record_t *tmp=VECTOR(*nattr)[(long int)VECTOR(news)[i]];
+      igraph_attribute_record_t *newrec=igraph_Calloc(1, igraph_attribute_record_t);
+      igraph_attribute_type_t type=tmp->type;
+      if (!newrec) {
+				IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+      }
+      IGRAPH_FINALLY(igraph_free, newrec);
+      newrec->type=type;
+      newrec->name=strdup(tmp->name);
+      if (!newrec->name) {
+				IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+      }
+      IGRAPH_FINALLY(igraph_free, (char*)newrec->name);
+      if (type==IGRAPH_ATTRIBUTE_STRING) {
+				bsvector_t *newstr=igraph_Calloc(1, bsvector_t);
+				if (!newstr) {
+					IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM);
+				}
+				IGRAPH_FINALLY(igraph_free, newstr);
+				BSVECTOR_INIT_FINALLY(newstr, origlen);
+				newrec->value=newstr;
+      }
+      IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, newrec));
+      IGRAPH_FINALLY_CLEAN(4);
+    }
+    ealno=igraph_vector_ptr_size(eal);
+  }
+
+  /* Now append the new values */
+  for (i=0; i<ealno; i++) {
+    igraph_attribute_record_t *oldrec=VECTOR(*eal)[i];
+    igraph_attribute_record_t *newrec=0;
+    const char *name=oldrec->name;
+    long int j;
+    igraph_bool_t l=0;
+    if (nattr) { l=igraph_haskell_attribute_find(nattr, name, &j); }
+    if (l) {
+      /* This attribute is present in nattr */
+      bsvector_t *oldstr, *newstr;
+      newrec=VECTOR(*nattr)[j];
+      oldstr=(bsvector_t*)oldrec->value;
+      newstr=(bsvector_t*)newrec->value;
+      if (oldrec->type != newrec->type) {
+				IGRAPH_ERROR("Attribute types do not match", IGRAPH_EINVAL);
+      }
+      if (oldrec->type == IGRAPH_ATTRIBUTE_STRING) {
+				if (ne != bsvector_size(newstr)) {
+					IGRAPH_ERROR("Invalid string attribute length", IGRAPH_EINVAL);
+				}
+				IGRAPH_CHECK(bsvector_append(oldstr, newstr));
+			} else {
+				IGRAPH_WARNING("Invalid attribute type");
+			}
+    } else {
+      /* No such attribute, append NA's */
+      bsvector_t *oldstr=(bsvector_t*)oldrec->value;
+      if (oldrec->type == IGRAPH_ATTRIBUTE_STRING) {
+				IGRAPH_CHECK(bsvector_resize(oldstr, origlen+ne));
+			} else {
+				IGRAPH_WARNING("Invalid attribute type");
+      }
+    }
+  }
+
+  igraph_vector_destroy(&news);
+  IGRAPH_FINALLY_CLEAN(1);
+
+  return 0;
+}
+
+int igraph_haskell_attribute_permute_edges(const igraph_t *graph,
+				      igraph_t *newgraph,
+				      const igraph_vector_t *idx) {
+
+  if (graph == newgraph) {
+
+    igraph_haskell_attributes_t *attr=graph->attr;
+    igraph_vector_ptr_t *eal=&attr->eal;
+    long int ealno=igraph_vector_ptr_size(eal);
+    long int i;
+
+    for (i=0; i<ealno; i++) {
+      igraph_attribute_record_t *oldrec=VECTOR(*eal)[i];
+      igraph_attribute_type_t type=oldrec->type;
+      bsvector_t *str, *newstr;
+      if (type == IGRAPH_ATTRIBUTE_STRING) {
+				str=(bsvector_t*)oldrec->value;
+				newstr=igraph_Calloc(1, bsvector_t);
+				if (!newstr) {
+					IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM);
+				}
+				IGRAPH_CHECK(bsvector_init(newstr, 0));
+				IGRAPH_FINALLY(bsvector_destroy, newstr);
+				bsvector_index(str, newstr, idx);
+				oldrec->value=newstr;
+				bsvector_destroy(str);
+				igraph_Free(str);
+				IGRAPH_FINALLY_CLEAN(1);
+			} else {
+				IGRAPH_WARNING("Unknown edge attribute ignored");
+      }
+    }
+
+  } else {
+
+    igraph_haskell_attributes_t *attr=graph->attr;
+    igraph_vector_ptr_t *eal=&attr->eal;
+    long int ealno=igraph_vector_ptr_size(eal);
+    long int i;
+
+    /* New edge attributes */
+    igraph_haskell_attributes_t *new_attr=newgraph->attr;
+    igraph_vector_ptr_t *new_eal=&new_attr->eal;
+    IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, ealno));
+
+    IGRAPH_FINALLY(igraph_haskell_attribute_permute_free, new_eal);
+
+    for (i=0; i<ealno; i++) {
+      igraph_attribute_record_t *oldrec=VECTOR(*eal)[i];
+      igraph_attribute_type_t type=oldrec->type;
+      bsvector_t *str, *newstr;
+
+      /* The record itself */
+      igraph_attribute_record_t *new_rec= igraph_Calloc(1, igraph_attribute_record_t);
+      if (!new_rec) {
+				IGRAPH_ERROR("Cannot create edge attributes", IGRAPH_ENOMEM);
+      }
+      new_rec->name = strdup(oldrec->name);
+      new_rec->type = oldrec->type;
+      VECTOR(*new_eal)[i] = new_rec;
+
+      if (type == IGRAPH_ATTRIBUTE_STRING) {
+				str=(bsvector_t*)oldrec->value;
+				newstr=igraph_Calloc(1, bsvector_t);
+				if (!newstr) {
+					IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM);
+				}
+				IGRAPH_CHECK(bsvector_init(newstr, 0));
+				IGRAPH_FINALLY(bsvector_destroy, newstr);
+				bsvector_index(str, newstr, idx);
+				new_rec->value=newstr;
+				IGRAPH_FINALLY_CLEAN(1);
+			} else {
+				IGRAPH_WARNING("Unknown edge attribute ignored");
+      }
+    }
+    IGRAPH_FINALLY_CLEAN(1);
+  }
+
+  return 0;
+}
+
+int igraph_haskell_attribute_combine_edges(const igraph_t *graph,
+			 igraph_t *newgraph,
+			 const igraph_vector_ptr_t *merges,
+			 const igraph_attribute_combination_t *comb) {
+
+  IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM);
+  return 1;
+}
+
+int igraph_haskell_attribute_get_info(const igraph_t *graph,
+				 igraph_strvector_t *gnames,
+				 igraph_vector_t *gtypes,
+				 igraph_strvector_t *vnames,
+				 igraph_vector_t *vtypes,
+				 igraph_strvector_t *enames,
+				 igraph_vector_t *etypes) {
+
+  igraph_strvector_t *names[3] = { gnames, vnames, enames };
+  igraph_vector_t *types[3] = { gtypes, vtypes, etypes };
+  igraph_haskell_attributes_t *at=graph->attr;
+  igraph_vector_ptr_t *attr[3]={ &at->gal, &at->val, &at->eal };
+  long int i,j;
+
+  for (i=0; i<3; i++) {
+    igraph_strvector_t *n=names[i];
+    igraph_vector_t *t=types[i];
+    igraph_vector_ptr_t *al=attr[i];
+    long int len=igraph_vector_ptr_size(al);
+
+    if (n) {
+      IGRAPH_CHECK(igraph_strvector_resize(n, len));
+    }
+    if (t) {
+      IGRAPH_CHECK(igraph_vector_resize(t, len));
+    }
+
+    for (j=0; j<len; j++) {
+      igraph_attribute_record_t *rec=VECTOR(*al)[j];
+      const char *name=rec->name;
+      igraph_attribute_type_t type=rec->type;
+      if (n) {
+	IGRAPH_CHECK(igraph_strvector_set(n, j, name));
+      }
+      if (t) {
+	VECTOR(*t)[j]=type;
+      }
+    }
+  }
+
+  return 0;
+}
+
+igraph_bool_t igraph_haskell_attribute_has_attr(const igraph_t *graph,
+					 igraph_attribute_elemtype_t type,
+					 const char *name) {
+  igraph_haskell_attributes_t *at=graph->attr;
+  igraph_vector_ptr_t *attr[3]={ &at->gal, &at->val, &at->eal };
+  long int attrnum;
+
+  switch (type) {
+  case IGRAPH_ATTRIBUTE_GRAPH:
+    attrnum=0;
+    break;
+  case IGRAPH_ATTRIBUTE_VERTEX:
+    attrnum=1;
+    break;
+  case IGRAPH_ATTRIBUTE_EDGE:
+    attrnum=2;
+    break;
+  default:
+    IGRAPH_ERROR("Unknown attribute element type", IGRAPH_EINVAL);
+    break;
+  }
+
+  return igraph_haskell_attribute_find(attr[attrnum], name, 0);
+}
+
+int igraph_haskell_attribute_gettype(const igraph_t *graph,
+			      igraph_attribute_type_t *type,
+			      igraph_attribute_elemtype_t elemtype,
+			      const char *name) {
+  long int attrnum;
+  igraph_attribute_record_t *rec;
+  igraph_haskell_attributes_t *at=graph->attr;
+  igraph_vector_ptr_t *attr[3]={ &at->gal, &at->val, &at->eal };
+  igraph_vector_ptr_t *al;
+  long int j;
+  igraph_bool_t l=0;
+
+  switch (elemtype) {
+  case IGRAPH_ATTRIBUTE_GRAPH:
+    attrnum=0;
+    break;
+  case IGRAPH_ATTRIBUTE_VERTEX:
+    attrnum=1;
+    break;
+  case IGRAPH_ATTRIBUTE_EDGE:
+    attrnum=2;
+    break;
+  default:
+    IGRAPH_ERROR("Unknown attribute element type", IGRAPH_EINVAL);
+    break;
+  }
+
+  al=attr[attrnum];
+  l=igraph_haskell_attribute_find(al, name, &j);
+  if (!l) {
+    IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+  }
+  rec=VECTOR(*al)[j];
+  *type=rec->type;
+
+  return 0;
+}
+
+int igraph_haskell_attribute_get_numeric_graph_attr(const igraph_t *graph,
+					      const char *name,
+					      igraph_vector_t *value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+int igraph_haskell_attribute_get_bool_graph_attr(const igraph_t *graph,
+					    const char *name,
+					    igraph_vector_bool_t *value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+int igraph_haskell_attribute_get_string_graph_attr(const igraph_t *graph,
+					     const char *name,
+					     igraph_strvector_t *value_) {
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *gal=&attr->gal;
+  long int j;
+  igraph_attribute_record_t *rec;
+  bsvector_t *str;
+  igraph_bool_t l=igraph_haskell_attribute_find(gal, name, &j);
+
+  if (!l) {
+    IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+  }
+
+  rec=VECTOR(*gal)[j];
+  str=(bsvector_t*)rec->value;
+
+	bsvector_t *value;
+	bsvector_init(value, 1);
+
+  IGRAPH_CHECK(bsvector_set(value, 0, BS(*str,0)));
+
+	igraph_strvector_copy(value_, bsvector_to_strvector(value));
+
+  return 0;
+}
+
+int igraph_haskell_attribute_get_numeric_vertex_attr(const igraph_t *graph,
+					      const char *name,
+					      igraph_vs_t vs,
+					      igraph_vector_t *value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+int igraph_haskell_attribute_get_bool_vertex_attr(const igraph_t *graph,
+					     const char *name,
+					     igraph_vs_t vs,
+					     igraph_vector_bool_t *value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+int igraph_haskell_attribute_get_string_vertex_attr(const igraph_t *graph,
+					     const char *name,
+					     igraph_vs_t vs,
+					     igraph_strvector_t *value_) {
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *val=&attr->val;
+  long int j;
+  igraph_attribute_record_t *rec;
+  bsvector_t *str;
+  igraph_bool_t l=igraph_haskell_attribute_find(val, name, &j);
+
+  if (!l) {
+    IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+  }
+
+	bsvector_t *value;
+	bsvector_init(value, 0);
+
+  rec=VECTOR(*val)[j];
+  str=(bsvector_t*)rec->value;
+  if (igraph_vs_is_all(&vs)) {
+    bsvector_resize(value, 0);
+    IGRAPH_CHECK(bsvector_append(value, str));
+  } else {
+    igraph_vit_t it;
+    long int i=0;
+    IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
+    IGRAPH_FINALLY(igraph_vit_destroy, &it);
+    IGRAPH_CHECK(bsvector_resize(value, IGRAPH_VIT_SIZE(it)));
+    for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) {
+      long int v=IGRAPH_VIT_GET(it);
+      bytestring_t *s;
+      bsvector_get(str, v, &s);
+      IGRAPH_CHECK(bsvector_set(value, i, s));
+    }
+    igraph_vit_destroy(&it);
+    IGRAPH_FINALLY_CLEAN(1);
+  }
+
+	igraph_strvector_copy(value_, bsvector_to_strvector(value));
+  return 0;
+}
+
+int igraph_haskell_attribute_get_numeric_edge_attr(const igraph_t *graph,
+					    const char *name,
+					    igraph_es_t es,
+					    igraph_vector_t *value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+int igraph_haskell_attribute_get_string_edge_attr(const igraph_t *graph,
+					   const char *name,
+					   igraph_es_t es,
+					   igraph_strvector_t *value_) {
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *eal=&attr->eal;
+  long int j;
+  igraph_attribute_record_t *rec;
+  bsvector_t *str;
+  igraph_bool_t l=igraph_haskell_attribute_find(eal, name, &j);
+
+  if (!l) {
+    IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL);
+  }
+
+	bsvector_t *value;
+	bsvector_init(value, 1);
+
+  rec=VECTOR(*eal)[j];
+  str=(bsvector_t*)rec->value;
+  if (igraph_es_is_all(&es)) {
+    bsvector_resize(value, 0);
+    IGRAPH_CHECK(bsvector_append(value, str));
+  } else {
+    igraph_eit_t it;
+    long int i=0;
+    IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
+    IGRAPH_FINALLY(igraph_eit_destroy, &it);
+    IGRAPH_CHECK(bsvector_resize(value, IGRAPH_EIT_SIZE(it)));
+    for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) {
+      long int e=IGRAPH_EIT_GET(it);
+      bytestring_t *s;
+      bsvector_get(str, e, &s);
+      IGRAPH_CHECK(bsvector_set(value, i, s));
+    }
+    igraph_eit_destroy(&it);
+    IGRAPH_FINALLY_CLEAN(1);
+  }
+
+	igraph_strvector_copy(value_, bsvector_to_strvector(value));
+  return 0;
+}
+
+int igraph_haskell_attribute_get_bool_edge_attr(const igraph_t *graph,
+					   const char *name,
+					   igraph_es_t es,
+					   igraph_vector_bool_t *value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+igraph_real_t igraph_haskell_attribute_GAN(const igraph_t *graph, const char *name) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_GAB
+ * Query a boolean graph attribute.
+ *
+ * Returns the value of the given numeric graph attribute.
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute to query.
+ * \return The value of the attribute.
+ *
+ * \sa \ref GAB for a simpler interface.
+ *
+ * Time complexity: O(Ag), the number of graph attributes.
+ */
+igraph_bool_t igraph_haskell_attribute_GAB(const igraph_t *graph, const char *name) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_GAS
+ * Query a string graph attribute.
+ *
+ * Returns a <type>const</type> pointer to the string graph attribute
+ * specified in \p name.
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute to query.
+ * \return The value of the attribute.
+ *
+ * \sa \ref GAS for a simpler interface.
+ *
+ * Time complexity: O(Ag), the number of graph attributes.
+ */
+const bytestring_t* igraph_haskell_attribute_GAS(const igraph_t *graph, const char *name) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *gal=&attr->gal;
+  long int j;
+  igraph_attribute_record_t *rec;
+  bsvector_t *str;
+  igraph_bool_t l=igraph_haskell_attribute_find(gal, name, &j);
+
+  if (!l) {
+    igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+    return 0;
+  }
+
+  rec=VECTOR(*gal)[j];
+  str=(bsvector_t*)rec->value;
+  return BS(*str, 0);
+}
+
+/**
+ * \function igraph_haskell_attribute_VAN
+ * Query a numeric vertex attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vid The id of the queried vertex.
+ * \return The value of the attribute.
+ *
+ * \sa \ref VAN macro for a simpler interface.
+ *
+ * Time complexity: O(Av), the number of vertex attributes.
+ */
+igraph_real_t igraph_haskell_attribute_VAN(const igraph_t *graph, const char *name,
+				      igraph_integer_t vid) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_VAB
+ * Query a boolean vertex attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vid The id of the queried vertex.
+ * \return The value of the attribute.
+ *
+ * \sa \ref VAB macro for a simpler interface.
+ *
+ * Time complexity: O(Av), the number of vertex attributes.
+ */
+igraph_bool_t igraph_haskell_attribute_VAB(const igraph_t *graph, const char *name,
+				    igraph_integer_t vid) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_VAS
+ * Query a string vertex attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vid The id of the queried vertex.
+ * \return The value of the attribute.
+ *
+ * \sa The macro \ref VAS for a simpler interface.
+ *
+ * Time complexity: O(Av), the number of vertex attributes.
+ */
+const bytestring_t* igraph_haskell_attribute_VAS(const igraph_t *graph, const char *name,
+				    igraph_integer_t vid) {
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *val=&attr->val;
+  long int j;
+  igraph_attribute_record_t *rec;
+  bsvector_t *str;
+  igraph_bool_t l=igraph_haskell_attribute_find(val, name, &j);
+
+  if (!l) {
+    igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+    return 0;
+  }
+
+  rec=VECTOR(*val)[j];
+  str=(bsvector_t*)rec->value;
+  return BS(*str, (long int)vid);
+}
+
+/**
+ * \function igraph_haskell_attribute_EAN
+ * Query a numeric edge attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param eid The id of the queried edge.
+ * \return The value of the attribute.
+ *
+ * \sa \ref EAN for an easier interface.
+ *
+ * Time complexity: O(Ae), the number of edge attributes.
+ */
+igraph_real_t igraph_haskell_attribute_EAN(const igraph_t *graph, const char *name,
+				      igraph_integer_t eid) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_EAB
+ * Query a boolean edge attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param eid The id of the queried edge.
+ * \return The value of the attribute.
+ *
+ * \sa \ref EAB for an easier interface.
+ *
+ * Time complexity: O(Ae), the number of edge attributes.
+ */
+igraph_bool_t igraph_haskell_attribute_EAB(const igraph_t *graph, const char *name,
+				    igraph_integer_t eid) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_EAS
+ * Query a string edge attribute.
+ *
+ * The attribute must exist, otherwise an error is triggered.
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param eid The id of the queried edge.
+ * \return The value of the attribute.
+ *
+ * \se \ref EAS if you want to type less.
+ *
+ * Time complexity: O(Ae), the number of edge attributes.
+ */
+const bytestring_t* igraph_haskell_attribute_EAS(const igraph_t *graph, const char *name,
+				    igraph_integer_t eid) {
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *eal=&attr->eal;
+  long int j;
+  igraph_attribute_record_t *rec;
+  bsvector_t *str;
+  igraph_bool_t l=igraph_haskell_attribute_find(eal, name, &j);
+
+  if (!l) {
+    igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL);
+    return 0;
+  }
+
+  rec=VECTOR(*eal)[j];
+  str=(bsvector_t*)rec->value;
+  return BS(*str, (long int)eid);
+}
+
+/**
+ * \function igraph_haskell_attribute_VANV
+ * Query a numeric vertex attribute for many vertices
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vids The vertices to query.
+ * \param result Pointer to an initialized vector, the result is
+ *    stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(v), where v is the number of vertices in 'vids'.
+ */
+
+int igraph_haskell_attribute_VANV(const igraph_t *graph, const char *name,
+			   igraph_vs_t vids, igraph_vector_t *result) {
+
+  return igraph_haskell_attribute_get_numeric_vertex_attr(graph, name, vids,
+						     result);
+}
+
+/**
+ * \function igraph_haskell_attribute_VABV
+ * Query a boolean vertex attribute for many vertices
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vids The vertices to query.
+ * \param result Pointer to an initialized boolean vector, the result is
+ *    stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(v), where v is the number of vertices in 'vids'.
+ */
+
+int igraph_haskell_attribute_VABV(const igraph_t *graph, const char *name,
+			   igraph_vs_t vids, igraph_vector_bool_t *result) {
+
+  return igraph_haskell_attribute_get_bool_vertex_attr(graph, name, vids,
+						  result);
+}
+
+/**
+ * \function igraph_haskell_attribute_EANV
+ * Query a numeric edge attribute for many edges
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param eids The edges to query.
+ * \param result Pointer to an initialized vector, the result is
+ *    stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(e), where e is the number of edges in 'eids'.
+ */
+
+int igraph_haskell_attribute_EANV(const igraph_t *graph, const char *name,
+			   igraph_es_t eids, igraph_vector_t *result) {
+
+  return igraph_haskell_attribute_get_numeric_edge_attr(graph, name, eids,
+						   result);
+}
+
+/**
+ * \function igraph_haskell_attribute_EABV
+ * Query a boolean edge attribute for many edges
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param eids The edges to query.
+ * \param result Pointer to an initialized boolean vector, the result is
+ *    stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(e), where e is the number of edges in 'eids'.
+ */
+
+int igraph_haskell_attribute_EABV(const igraph_t *graph, const char *name,
+			   igraph_es_t eids, igraph_vector_bool_t *result) {
+
+  return igraph_haskell_attribute_get_bool_edge_attr(graph, name, eids,
+						result);
+}
+
+/**
+ * \function igraph_haskell_attribute_VASV
+ * Query a string vertex attribute for many vertices
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vids The vertices to query.
+ * \param result Pointer to an initialized string vector, the result
+ *     is stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(v), where v is the number of vertices in 'vids'.
+ * (We assume that the string attributes have a bounded length.)
+ */
+
+int igraph_haskell_attribute_VASV(const igraph_t *graph, const char *name,
+			   igraph_vs_t vids, igraph_strvector_t *result) {
+
+  return igraph_haskell_attribute_get_string_vertex_attr(graph, name, vids,
+						    result);
+}
+
+/**
+ * \function igraph_haskell_attribute_EASV
+ * Query a string edge attribute for many edges
+ *
+ * \param graph The input graph.
+ * \param name The name of the attribute.
+ * \param vids The edges to query.
+ * \param result Pointer to an initialized string vector, the result
+ *     is stored here. It will be resized, if needed.
+ * \return Error code.
+ *
+ * Time complexity: O(e), where e is the number of edges in
+ * 'eids'. (We assume that the string attributes have a bounded length.)
+ */
+
+int igraph_haskell_attribute_EASV(const igraph_t *graph, const char *name,
+			   igraph_es_t eids, igraph_strvector_t *result) {
+
+  return igraph_haskell_attribute_get_string_edge_attr(graph, name, eids,
+						   result);
+}
+
+/**
+ * \function igraph_haskell_attribute_list
+ * List all attributes
+ *
+ * See \ref igraph_attribute_type_t for the various attribute types.
+ * \param graph The input graph.
+ * \param gnames String vector, the names of the graph attributes.
+ * \param gtypes Numeric vector, the types of the graph attributes.
+ * \param vnames String vector, the names of the vertex attributes.
+ * \param vtypes Numeric vector, the types of the vertex attributes.
+ * \param enames String vector, the names of the edge attributes.
+ * \param etypes Numeric vector, the types of the edge attributes.
+ * \return Error code.
+ *
+ * Naturally, the string vector with the attribute names and the
+ * numeric vector with the attribute types are in the right order,
+ * i.e. the first name corresponds to the first type, etc.
+ *
+ * Time complexity: O(Ag+Av+Ae), the number of all attributes.
+ */
+int igraph_haskell_attribute_list(const igraph_t *graph,
+			   igraph_strvector_t *gnames, igraph_vector_t *gtypes,
+			   igraph_strvector_t *vnames, igraph_vector_t *vtypes,
+			   igraph_strvector_t *enames, igraph_vector_t *etypes) {
+  return igraph_haskell_attribute_get_info(graph, gnames, gtypes, vnames, vtypes,
+				      enames, etypes);
+}
+
+/**
+ * \function igraph_haskell_attribute_has_attr
+ * Checks whether a (graph, vertex or edge) attribute exists
+ *
+ * \param graph The graph.
+ * \param type The type of the attribute, \c IGRAPH_ATTRIBUTE_GRAPH,
+ *        \c IGRAPH_ATTRIBUTE_VERTEX or \c IGRAPH_ATTRIBUTE_EDGE.
+ * \param name Character constant, the name of the attribute.
+ * \return Logical value, TRUE if the attribute exists, FALSE otherwise.
+ *
+ * Time complexity: O(A), the number of (graph, vertex or edge)
+ * attributes, assuming attribute names are not too long.
+igraph_bool_t igraph_haskell_attribute_has_attr(const igraph_t *graph,
+					 igraph_attribute_elemtype_t type,
+					 const char *name) {
+  return igraph_haskell_attribute_has_attr(graph, type, name);
+}
+ */
+
+/**
+ * \function igraph_haskell_attribute_GAN_set
+ * Set a numeric graph attribute
+ *
+ * \param graph The graph.
+ * \param name Name of the graph attribute. If there is no such
+ *   attribute yet, then it will be added.
+ * \param value The (new) value of the graph attribute.
+ * \return Error code.
+ *
+ * \se \ref SETGAN if you want to type less.
+ *
+ * Time complexity: O(1).
+ */
+int igraph_haskell_attribute_GAN_set(igraph_t *graph, const char *name,
+			      igraph_real_t value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_GAB_set
+ * Set a boolean graph attribute
+ *
+ * \param graph The graph.
+ * \param name Name of the graph attribute. If there is no such
+ *   attribute yet, then it will be added.
+ * \param value The (new) value of the graph attribute.
+ * \return Error code.
+ *
+ * \se \ref SETGAN if you want to type less.
+ *
+ * Time complexity: O(1).
+ */
+int igraph_haskell_attribute_GAB_set(igraph_t *graph, const char *name,
+			      igraph_bool_t value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_GAS_set
+ * Set a string graph attribute.
+ *
+ * \param graph The graph.
+ * \param name Name of the graph attribute. If there is no such
+ *   attribute yet, then it will be added.
+ * \param value The (new) value of the graph attribute. It will be
+ *   copied.
+ * \return Error code.
+ *
+ * \se \ref SETGAS if you want to type less.
+ *
+ * Time complexity: O(1).
+ */
+int igraph_haskell_attribute_GAS_set(igraph_t *graph, const char *name,
+			      const bytestring_t *value) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *gal=&attr->gal;
+  long int j;
+  igraph_bool_t l=igraph_haskell_attribute_find(gal, name, &j);
+
+  if (l) {
+    igraph_attribute_record_t *rec=VECTOR(*gal)[j];
+    if (rec->type != IGRAPH_ATTRIBUTE_STRING) {
+      IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+    } else {
+      bsvector_t *str=(bsvector_t*)rec->value;
+      IGRAPH_CHECK(bsvector_set(str, 0, value));
+    }
+  } else {
+    igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t);
+    bsvector_t *str;
+    if (!rec) {
+      IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, rec);
+    rec->name=strdup(name);
+    if (!rec->name) {
+      IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+    rec->type=IGRAPH_ATTRIBUTE_STRING;
+    str=igraph_Calloc(1, bsvector_t);
+    if (!str) {
+      IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, str);
+    BSVECTOR_INIT_FINALLY(str, 1);
+    IGRAPH_CHECK(bsvector_set(str, 0, value));
+    rec->value=str;
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec));
+    IGRAPH_FINALLY_CLEAN(4);
+  }
+
+  return 0;
+}
+
+/**
+ * \function igraph_haskell_attribute_VAN_set
+ * Set a numeric vertex attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all vertices
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param vid Vertices for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETVAN for a simpler way.
+ *
+ * Time complexity: O(n), the number of vertices if the attribute is
+ * new, O(|vid|) otherwise.
+ */
+int igraph_haskell_attribute_VAN_set(igraph_t *graph, const char *name,
+			      igraph_integer_t vid, igraph_real_t value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_VAB_set
+ * Set a boolean vertex attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all vertices
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param vid Vertices for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETVAB for a simpler way.
+ *
+ * Time complexity: O(n), the number of vertices if the attribute is
+ * new, O(|vid|) otherwise.
+ */
+int igraph_haskell_attribute_VAB_set(igraph_t *graph, const char *name,
+			      igraph_integer_t vid, igraph_bool_t value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_VAS_set
+ * Set a string vertex attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all vertices
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param vid Vertices for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETVAS for a simpler way.
+ *
+ * Time complexity: O(n*l), n is the number of vertices, l is the
+ * length of the string to set. If the attribute if not new then only
+ * O(|vid|*l).
+ */
+int igraph_haskell_attribute_VAS_set(igraph_t *graph, const char *name,
+			      igraph_integer_t vid, const bytestring_t *value) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *val=&attr->val;
+  long int j;
+  igraph_bool_t l=igraph_haskell_attribute_find(val, name, &j);
+
+  if (l) {
+    igraph_attribute_record_t *rec=VECTOR(*val)[j];
+    if (rec->type != IGRAPH_ATTRIBUTE_STRING) {
+      IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+    } else {
+      bsvector_t *str=(bsvector_t*)rec->value;
+      IGRAPH_CHECK(bsvector_set(str, vid, value));
+    }
+  } else {
+    igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t);
+    bsvector_t *str;
+    if (!rec) {
+      IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, rec);
+    rec->name=strdup(name);
+    if (!rec->name) {
+      IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+    rec->type=IGRAPH_ATTRIBUTE_STRING;
+    str=igraph_Calloc(1, bsvector_t);
+    if (!str) {
+      IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, str);
+    BSVECTOR_INIT_FINALLY(str, igraph_vcount(graph));
+    IGRAPH_CHECK(bsvector_set(str, vid, value));
+    rec->value=str;
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec));
+    IGRAPH_FINALLY_CLEAN(4);
+  }
+
+  return 0;
+}
+
+/**
+ * \function igraph_haskell_attribute_EAN_set
+ * Set a numeric edge attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all edges
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param eid Edges for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETEAN for a simpler way.
+ *
+ * Time complexity: O(e), the number of edges if the attribute is
+ * new, O(|eid|) otherwise.
+ */
+int igraph_haskell_attribute_EAN_set(igraph_t *graph, const char *name,
+			      igraph_integer_t eid, igraph_real_t value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_EAB_set
+ * Set a boolean edge attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all edges
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param eid Edges for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETEAB for a simpler way.
+ *
+ * Time complexity: O(e), the number of edges if the attribute is
+ * new, O(|eid|) otherwise.
+ */
+int igraph_haskell_attribute_EAB_set(igraph_t *graph, const char *name,
+			      igraph_integer_t eid, igraph_bool_t value) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_EAS_set
+ * Set a string edge attribute
+ *
+ * The attribute will be added if not present already. If present it
+ * will be overwritten. The same \p value is set for all edges
+ * included in \p vid.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param eid Edges for which to set the attribute.
+ * \param value The (new) value of the attribute.
+ * \return Error code.
+ *
+ * \sa \ref SETEAS for a simpler way.
+ *
+ * Time complexity: O(e*l), n is the number of edges, l is the
+ * length of the string to set. If the attribute if not new then only
+ * O(|eid|*l).
+ */
+int igraph_haskell_attribute_EAS_set(igraph_t *graph, const char *name,
+			      igraph_integer_t eid, const bytestring_t *value) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *eal=&attr->eal;
+  long int j;
+  igraph_bool_t l=igraph_haskell_attribute_find(eal, name, &j);
+
+  if (l) {
+    igraph_attribute_record_t *rec=VECTOR(*eal)[j];
+    if (rec->type != IGRAPH_ATTRIBUTE_STRING) {
+      IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL);
+    } else {
+      bsvector_t *str=(bsvector_t*)rec->value;
+      IGRAPH_CHECK(bsvector_set(str, eid, value));
+    }
+  } else {
+    igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t);
+    bsvector_t *str;
+    if (!rec) {
+      IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, rec);
+    rec->name=strdup(name);
+    if (!rec->name) {
+      IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+    rec->type=IGRAPH_ATTRIBUTE_STRING;
+    str=igraph_Calloc(1, bsvector_t);
+    if (!str) {
+      IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, str);
+    BSVECTOR_INIT_FINALLY(str, igraph_ecount(graph));
+    IGRAPH_CHECK(bsvector_set(str, eid, value));
+    rec->value=str;
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec));
+    IGRAPH_FINALLY_CLEAN(4);
+  }
+
+  return 0;
+}
+
+/**
+ * \function igraph_haskell_attribute_VAN_setv
+ * Set a numeric vertex attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param v The new attribute values. The length of this vector must
+ *   match the number of vertices.
+ * \return Error code.
+ *
+ * \sa \ref SETVANV for a simpler way.
+ *
+ * Time complexity: O(n), the number of vertices.
+ */
+
+int igraph_haskell_attribute_VAN_setv(igraph_t *graph, const char *name,
+			       const igraph_vector_t *v) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+/**
+ * \function igraph_haskell_attribute_VAB_setv
+ * Set a boolean vertex attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param v The new attribute values. The length of this boolean vector must
+ *   match the number of vertices.
+ * \return Error code.
+ *
+ * \sa \ref SETVANV for a simpler way.
+ *
+ * Time complexity: O(n), the number of vertices.
+ */
+
+int igraph_haskell_attribute_VAB_setv(igraph_t *graph, const char *name,
+			       const igraph_vector_bool_t *v) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_VAS_setv
+ * Set a string vertex attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param sv String vector, the new attribute values. The length of this vector must
+ *   match the number of vertices.
+ * \return Error code.
+ *
+ * \sa \ref SETVASV for a simpler way.
+ *
+ * Time complexity: O(n+l), n is the number of vertices, l is the
+ * total length of the strings.
+ */
+int igraph_haskell_attribute_VAS_setv(igraph_t *graph, const char *name,
+			       const bsvector_t *sv) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *val=&attr->val;
+  long int j;
+  igraph_bool_t l=igraph_haskell_attribute_find(val, name, &j);
+
+  /* Check length first */
+  if (bsvector_size(sv) != igraph_vcount(graph)) {
+    IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL);
+  }
+
+  if (l) {
+    /* Already present, check type */
+    igraph_attribute_record_t *rec=VECTOR(*val)[j];
+    bsvector_t *str=(bsvector_t *)rec->value;
+    if (rec->type != IGRAPH_ATTRIBUTE_STRING) {
+      IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL);
+    }
+    bsvector_clear(str);
+    IGRAPH_CHECK(bsvector_append(str, sv));
+  } else {
+    /* Add it */
+    igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t);
+    bsvector_t *str;
+    if (!rec) {
+      IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, rec);
+    rec->type=IGRAPH_ATTRIBUTE_STRING;
+    rec->name=strdup(name);
+    if (!rec->name) {
+      IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+    str=igraph_Calloc(1, bsvector_t);
+    if (!str) {
+      IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, str);
+    rec->value=str;
+    IGRAPH_CHECK(bsvector_copy(str, sv));
+    IGRAPH_FINALLY(bsvector_destroy, str);
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec));
+    IGRAPH_FINALLY_CLEAN(4);
+  }
+
+  return 0;
+}
+
+/**
+ * \function igraph_haskell_attribute_EAN_setv
+ * Set a numeric edge attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param v The new attribute values. The length of this vector must
+ *   match the number of edges.
+ * \return Error code.
+ *
+ * \sa \ref SETEANV for a simpler way.
+ *
+ * Time complexity: O(e), the number of edges.
+ */
+int igraph_haskell_attribute_EAN_setv(igraph_t *graph, const char *name,
+			       const igraph_vector_t *v) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_EAB_setv
+ * Set a boolean edge attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param v The new attribute values. The length of this vector must
+ *   match the number of edges.
+ * \return Error code.
+ *
+ * \sa \ref SETEABV for a simpler way.
+ *
+ * Time complexity: O(e), the number of edges.
+ */
+int igraph_haskell_attribute_EAB_setv(igraph_t *graph, const char *name,
+			       const igraph_vector_bool_t *v) {
+  IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM);
+  return 1;
+}
+
+/**
+ * \function igraph_haskell_attribute_EAS_setv
+ * Set a string edge attribute for all vertices.
+ *
+ * The attribute will be added if not present yet.
+ * \param graph The graph.
+ * \param name Name of the attribute.
+ * \param sv String vector, the new attribute values. The length of this vector must
+ *   match the number of edges.
+ * \return Error code.
+ *
+ * \sa \ref SETEASV for a simpler way.
+ *
+ * Time complexity: O(e+l), e is the number of edges, l is the
+ * total length of the strings.
+ */
+int igraph_haskell_attribute_EAS_setv(igraph_t *graph, const char *name,
+			       const bsvector_t *sv) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *eal=&attr->eal;
+  long int j;
+  igraph_bool_t l=igraph_haskell_attribute_find(eal, name, &j);
+
+  /* Check length first */
+  if (bsvector_size(sv) != igraph_ecount(graph)) {
+    IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL);
+  }
+
+  if (l) {
+    /* Already present, check type */
+    igraph_attribute_record_t *rec=VECTOR(*eal)[j];
+    bsvector_t *str=(bsvector_t *)rec->value;
+    if (rec->type != IGRAPH_ATTRIBUTE_STRING) {
+      IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL);
+    }
+    bsvector_clear(str);
+    IGRAPH_CHECK(bsvector_append(str, sv));
+  } else {
+    /* Add it */
+    igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t);
+    bsvector_t *str;
+    if (!rec) {
+      IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, rec);
+    rec->type=IGRAPH_ATTRIBUTE_STRING;
+    rec->name=strdup(name);
+    if (!rec->name) {
+      IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, (char*)rec->name);
+    str=igraph_Calloc(1, bsvector_t);
+    if (!str) {
+      IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM);
+    }
+    IGRAPH_FINALLY(igraph_free, str);
+    rec->value=str;
+    IGRAPH_CHECK(bsvector_copy(str, sv));
+    IGRAPH_FINALLY(bsvector_destroy, str);
+    IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec));
+    IGRAPH_FINALLY_CLEAN(4);
+  }
+
+  return 0;
+}
+
+void igraph_haskell_attribute_free_rec(igraph_attribute_record_t *rec) {
+
+  if (rec->type==IGRAPH_ATTRIBUTE_NUMERIC) {
+    igraph_vector_t *num=(igraph_vector_t*)rec->value;
+    igraph_vector_destroy(num);
+  } else if (rec->type==IGRAPH_ATTRIBUTE_STRING) {
+    bsvector_t *str=(bsvector_t*)rec->value;
+    bsvector_destroy(str);
+  } else if (rec->type==IGRAPH_ATTRIBUTE_BOOLEAN) {
+    igraph_vector_bool_t *boolvec=(igraph_vector_bool_t*)rec->value;
+    igraph_vector_bool_destroy(boolvec);
+  }
+  igraph_Free(rec->name);
+  igraph_Free(rec->value);
+  igraph_Free(rec);
+}
+
+/**
+ * \function igraph_haskell_attribute_remove_g
+ * Remove a graph attribute
+ *
+ * \param graph The graph object.
+ * \param name Name of the graph attribute to remove.
+ *
+ * \sa \ref DELGA for a simpler way.
+ *
+ */
+void igraph_haskell_attribute_remove_g(igraph_t *graph, const char *name) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *gal=&attr->gal;
+  long int j;
+  igraph_bool_t l=igraph_haskell_attribute_find(gal, name, &j);
+
+  if (l) {
+    igraph_haskell_attribute_free_rec(VECTOR(*gal)[j]);
+    igraph_vector_ptr_remove(gal, j);
+  } else {
+    IGRAPH_WARNING("Cannot remove non-existent graph attribute");
+  }
+}
+
+/**
+ * \function igraph_haskell_attribute_remove_v
+ * Remove a vertex attribute
+ *
+ * \param graph The graph object.
+ * \param name Name of the vertex attribute to remove.
+ *
+ * \sa \ref DELVA for a simpler way.
+ *
+ */
+void igraph_haskell_attribute_remove_v(igraph_t *graph, const char *name) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *val=&attr->val;
+  long int j;
+  igraph_bool_t l=igraph_haskell_attribute_find(val, name, &j);
+
+  if (l) {
+    igraph_haskell_attribute_free_rec(VECTOR(*val)[j]);
+    igraph_vector_ptr_remove(val, j);
+  } else {
+    IGRAPH_WARNING("Cannot remove non-existent graph attribute");
+  }
+}
+
+/**
+ * \function igraph_haskell_attribute_remove_e
+ * Remove an edge attribute
+ *
+ * \param graph The graph object.
+ * \param name Name of the edge attribute to remove.
+ *
+ * \sa \ref DELEA for a simpler way.
+ *
+ */
+void igraph_haskell_attribute_remove_e(igraph_t *graph, const char *name) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+  igraph_vector_ptr_t *eal=&attr->eal;
+  long int j;
+  igraph_bool_t l=igraph_haskell_attribute_find(eal, name, &j);
+
+  if (l) {
+    igraph_haskell_attribute_free_rec(VECTOR(*eal)[j]);
+    igraph_vector_ptr_remove(eal, j);
+  } else {
+    IGRAPH_WARNING("Cannot remove non-existent graph attribute");
+  }
+}
+
+/**
+ * \function igraph_haskell_attribute_remove_all
+ * Remove all graph/vertex/edge attributes
+ *
+ * \param graph The graph object.
+ * \param g Boolean, whether to remove graph attributes.
+ * \param v Boolean, whether to remove vertex attributes.
+ * \param e Boolean, whether to remove edge attributes.
+ *
+ * \sa \ref DELGAS, \ref DELVAS, \ref DELEAS, \ref DELALL for simpler
+ * ways.
+ */
+void igraph_haskell_attribute_remove_all(igraph_t *graph, igraph_bool_t g,
+				  igraph_bool_t v, igraph_bool_t e) {
+
+  igraph_haskell_attributes_t *attr=graph->attr;
+
+  if (g) {
+    igraph_vector_ptr_t *gal=&attr->gal;
+    long int i, n=igraph_vector_ptr_size(gal);
+    for (i=0;i<n;i++) {
+      igraph_haskell_attribute_free_rec(VECTOR(*gal)[i]);
+    }
+    igraph_vector_ptr_clear(gal);
+  }
+  if (v) {
+    igraph_vector_ptr_t *val=&attr->val;
+    long int i, n=igraph_vector_ptr_size(val);
+    for (i=0;i<n;i++) {
+      igraph_haskell_attribute_free_rec(VECTOR(*val)[i]);
+    }
+    igraph_vector_ptr_clear(val);
+  }
+  if (e) {
+    igraph_vector_ptr_t *eal=&attr->eal;
+    long int i, n=igraph_vector_ptr_size(eal);
+    for (i=0;i<n;i++) {
+      igraph_haskell_attribute_free_rec(VECTOR(*eal)[i]);
+    }
+    igraph_vector_ptr_clear(eal);
+  }
+}
diff --git a/cbits/haskell_igraph.c b/cbits/haskell_igraph.c
new file mode 100644
--- /dev/null
+++ b/cbits/haskell_igraph.c
@@ -0,0 +1,28 @@
+#include <igraph/igraph.h>
+#include "haskell_attributes.h"
+
+const igraph_attribute_table_t igraph_haskell_attribute_table={
+  &igraph_haskell_attribute_init, &igraph_haskell_attribute_destroy,
+  &igraph_haskell_attribute_copy, &igraph_haskell_attribute_add_vertices,
+  &igraph_haskell_attribute_permute_vertices,
+  &igraph_haskell_attribute_combine_vertices, &igraph_haskell_attribute_add_edges,
+  &igraph_haskell_attribute_permute_edges,
+  &igraph_haskell_attribute_combine_edges,
+  &igraph_haskell_attribute_get_info,
+  &igraph_haskell_attribute_has_attr, &igraph_haskell_attribute_gettype,
+  &igraph_haskell_attribute_get_numeric_graph_attr,
+  &igraph_haskell_attribute_get_string_graph_attr,
+  &igraph_haskell_attribute_get_bool_graph_attr,
+  &igraph_haskell_attribute_get_numeric_vertex_attr,
+  &igraph_haskell_attribute_get_string_vertex_attr,
+  &igraph_haskell_attribute_get_bool_vertex_attr,
+  &igraph_haskell_attribute_get_numeric_edge_attr,
+  &igraph_haskell_attribute_get_string_edge_attr,
+  &igraph_haskell_attribute_get_bool_edge_attr
+};
+
+void haskelligraph_init()
+{
+  /* attach attribute table */
+  igraph_i_set_attribute_table(&igraph_haskell_attribute_table);
+}
diff --git a/cbits/haskelligraph.c b/cbits/haskelligraph.c
deleted file mode 100644
--- a/cbits/haskelligraph.c
+++ /dev/null
@@ -1,36 +0,0 @@
-#include <igraph/igraph.h>
-
-igraph_integer_t igraph_get_eid_(igraph_t* graph, igraph_integer_t pfrom, igraph_integer_t pto,
-           igraph_bool_t directed, igraph_bool_t error)
-{
-  igraph_integer_t eid;
-  igraph_get_eid(graph, &eid, pfrom, pto, directed, error);
-  return eid;
-}
-
-char** igraph_strvector_get_(igraph_strvector_t* s, long int i)
-{
-  char** x = (char**) malloc (sizeof(char*));
-  igraph_strvector_get(s, i, x);
-  return x;
-}
-
-igraph_arpack_options_t* igraph_arpack_new()
-{
-  igraph_arpack_options_t *arpack = (igraph_arpack_options_t*) malloc(sizeof(igraph_arpack_options_t));
-  igraph_arpack_options_init(arpack);
-  return arpack;
-}
-
-void igraph_arpack_destroy(igraph_arpack_options_t* arpack)
-{
-  if (arpack)
-    free(arpack);
-  arpack = NULL;
-}
-
-void haskelligraph_init()
-{
-  /* attach attribute table */
-  igraph_i_set_attribute_table(&igraph_cattribute_table);
-}
diff --git a/cbits/haskelligraph.h b/cbits/haskelligraph.h
deleted file mode 100644
--- a/cbits/haskelligraph.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef HASKELL_IGRAPH
-#define HASKELL_IGRAPH
-
-#include <igraph/igraph.h>
-
-igraph_integer_t igraph_get_eid_(igraph_t* graph, igraph_integer_t pfrom, igraph_integer_t pto,
-           igraph_bool_t directed, igraph_bool_t error);
-
-char** igraph_strvector_get_(igraph_strvector_t* s, long int i);
-
-igraph_arpack_options_t* igraph_arpack_new();
-
-void igraph_arpack_destroy(igraph_arpack_options_t* arpack);
-
-void haskelligraph_init();
-
-#endif
diff --git a/haskell-igraph.cabal b/haskell-igraph.cabal
--- a/haskell-igraph.cabal
+++ b/haskell-igraph.cabal
@@ -1,20 +1,23 @@
--- Initial igraph-bindings.cabal generated by cabal init.  For further
--- documentation, see http://haskell.org/cabal/users-guide/
-
 name:                haskell-igraph
-version:             0.3.0
-synopsis:            Imcomplete igraph bindings
-description:         This is an attempt to create a complete bindings for the
-                     igraph<"http://igraph.org/c/"> library.
+version:             0.4.0
+synopsis:            Haskell interface of the igraph library.
+description:         igraph<"http://igraph.org/c/"> is a library for creating
+                     and manipulating large graphs. This package provides the Haskell
+                     interface of igraph.
 license:             MIT
 license-file:        LICENSE
 author:              Kai Zhang
 maintainer:          kai@kzhang.org
-copyright:           (c) 2016 Kai Zhang
+copyright:           (c) 2016-2018 Kai Zhang
 category:            Math
 build-type:          Simple
 cabal-version:       >=1.24
-extra-source-files:  cbits/haskelligraph.h
+extra-source-files:
+  include/haskell_igraph.h
+  include/bytestring.h
+  include/haskell_attributes.h
+  README.md
+  ChangeLog.md
 
 Flag graphics
   Description: Enable graphics output
@@ -22,6 +25,18 @@
 
 library
   exposed-modules:
+    IGraph
+    IGraph.Types
+    IGraph.Mutable
+    IGraph.Clique
+    IGraph.Structure
+    IGraph.Isomorphism
+    IGraph.Community
+    IGraph.Read
+    IGraph.Motif
+    IGraph.Layout
+    IGraph.Generators
+    IGraph.Exporter.GEXF
     IGraph.Internal.Initialization
     IGraph.Internal.Constants
     IGraph.Internal.Arpack
@@ -35,18 +50,10 @@
     IGraph.Internal.Clique
     IGraph.Internal.Community
     IGraph.Internal.Layout
-    IGraph
-    IGraph.Mutable
-    IGraph.Clique
-    IGraph.Structure
-    IGraph.Isomorphism
-    IGraph.Community
-    IGraph.Read
-    IGraph.Motif
-    IGraph.Layout
-    IGraph.Generators
-    IGraph.Exporter.GEXF
 
+  other-modules:
+    IGraph.Internal.C2HS
+
   if flag(graphics)
     exposed-modules: IGraph.Exporter.Graphics
 
@@ -54,11 +61,13 @@
     build-depends: diagrams-lib, diagrams-cairo
 
   build-depends:
-      base >=4.0 && <5.0
-    , binary
-    , bytestring >=0.9
-    , bytestring-lexing >=0.5
+      base >= 4.0 && < 5.0
+    , bytestring >= 0.9
+    , bytestring-lexing >= 0.5
+    , cereal
+    , cereal-conduit
     , colour
+    , conduit >= 1.3.0
     , primitive
     , unordered-containers
     , hashable
@@ -70,8 +79,11 @@
   hs-source-dirs:      src
   default-language:    Haskell2010
   build-tools:         c2hs >=0.25.0
-  c-sources:           cbits/haskelligraph.c
-  include-dirs:        cbits
+  c-sources:
+    cbits/haskell_igraph.c
+    cbits/haskell_attributes.c
+    cbits/bytestring.c
+  include-dirs:        include
 
 test-suite tests
   type: exitcode-stdio-1.0
@@ -79,6 +91,7 @@
   main-is: test.hs
   other-modules:
     Test.Basic
+    Test.Attributes
     Test.Structure
     Test.Isomorphism
     Test.Motif
@@ -88,6 +101,8 @@
   build-depends:
       base
     , haskell-igraph
+    , cereal
+    , conduit >= 1.3.0
     , data-ordlist
     , matrices
     , tasty
diff --git a/include/bytestring.h b/include/bytestring.h
new file mode 100644
--- /dev/null
+++ b/include/bytestring.h
@@ -0,0 +1,111 @@
+#ifndef HASKELL_IGRAPH_BYTESTRING
+#define HASKELL_IGRAPH_BYTESTRING
+
+#include <igraph/igraph.h>
+
+typedef struct bytestring_t {
+  unsigned long int len;
+  char *value;
+} bytestring_t;
+
+typedef struct bsvector_t {
+  bytestring_t **data;
+  long int len;
+} bsvector_t;
+
+#define BSVECTOR_INIT_FINALLY(v, size) \
+  do { IGRAPH_CHECK(bsvector_init(v, size)); \
+  IGRAPH_FINALLY( (igraph_finally_func_t*) bsvector_destroy, v); } while (0)
+
+/**
+ * \define STR
+ * Indexing string vectors
+ *
+ * This is a macro which allows to query the elements of a string vector in
+ * simpler way than \ref igraph_strvector_get(). Note this macro cannot be
+ * used to set an element, for that use \ref igraph_strvector_set().
+ * \param sv The string vector
+ * \param i The the index of the element.
+ * \return The element at position \p i.
+ *
+ * Time complexity: O(1).
+ */
+#define BS(sv,i) ((const bytestring_t *)((sv).data[(i)]))
+
+int bsvector_init(bsvector_t *sv, long int len);
+
+void bsvector_destroy(bsvector_t *sv);
+
+void bsvector_get(const bsvector_t *sv, long int idx, bytestring_t **value);
+
+int bsvector_set(bsvector_t *sv, long int idx, const bytestring_t *value);
+
+void bsvector_remove_section(bsvector_t *v, long int from, long int to);
+
+void bsvector_remove(bsvector_t *v, long int elem);
+
+/*
+void bsvector_move_interval(bsvector_t *v, long int begin,
+				   long int end, long int to) {
+  long int i;
+  assert(v != 0);
+  assert(v->data != 0);
+  for (i=to; i<to+end-begin; i++) {
+    if (v->data[i] != 0) {
+      destroy_bytestring(v->data[i]);
+    }
+  }
+  for (i=0; i<end-begin; i++) {
+    if (v->data[begin+i] != 0) {
+      size_t len=strlen(v->data[begin+i])+1;
+      v->data[to+i]=igraph_Calloc(len, char);
+      memcpy(v->data[to+i], v->data[begin+i], sizeof(char)*len);
+    }
+  }
+}
+*/
+
+int bsvector_copy(bsvector_t *to, const bsvector_t *from);
+
+int bsvector_append(bsvector_t *to, const bsvector_t *from);
+
+void bsvector_clear(bsvector_t *sv);
+
+int bsvector_resize(bsvector_t* v, long int newsize);
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_permdelete
+ * \brief Removes elements from a string vector (for internal use)
+ */
+
+void bsvector_permdelete(bsvector_t *v, const igraph_vector_t *index,
+				long int nremove);
+
+/**
+ * \ingroup strvector
+ * \function igraph_strvector_remove_negidx
+ * \brief Removes elements from a string vector (for internal use)
+ */
+
+void bsvector_remove_negidx(bsvector_t *v, const igraph_vector_t *neg,
+				   long int nremove);
+
+int bsvector_index(const bsvector_t *v, bsvector_t *newv,
+                   const igraph_vector_t *idx);
+
+long int bsvector_size(const bsvector_t *sv);
+
+bytestring_t* new_bytestring(int n);
+
+void destroy_bytestring(bytestring_t* str);
+
+char* bytestring_to_char(bytestring_t* from);
+
+bytestring_t* char_to_bytestring(char* from);
+
+igraph_strvector_t* bsvector_to_strvector(bsvector_t* from);
+
+bsvector_t* strvector_to_bsvector(igraph_strvector_t* from);
+
+#endif
diff --git a/include/haskell_attributes.h b/include/haskell_attributes.h
new file mode 100644
--- /dev/null
+++ b/include/haskell_attributes.h
@@ -0,0 +1,218 @@
+#ifndef HASKELL_IGRAPH_ATTRIBUTE
+#define HASKELL_IGRAPH_ATTRIBUTE
+
+#include "igraph/igraph.h"
+#include "bytestring.h"
+
+#include <string.h>
+
+igraph_bool_t igraph_haskell_attribute_find(const igraph_vector_ptr_t *ptrvec,
+				       const char *name, long int *idx);
+
+typedef struct igraph_haskell_attributes_t {
+  igraph_vector_ptr_t gal;
+  igraph_vector_ptr_t val;
+  igraph_vector_ptr_t eal;
+} igraph_haskell_attributes_t;
+
+int igraph_haskell_attributes_copy_attribute_record(igraph_attribute_record_t **newrec,
+					       const igraph_attribute_record_t *rec);
+
+
+int igraph_haskell_attribute_init(igraph_t *graph, igraph_vector_ptr_t *attr);
+
+void igraph_haskell_attribute_destroy(igraph_t *graph);
+
+void igraph_haskell_attribute_copy_free(igraph_haskell_attributes_t *attr);
+
+int igraph_haskell_attribute_copy(igraph_t *to, const igraph_t *from,
+			     igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea);
+
+int igraph_haskell_attribute_add_vertices(igraph_t *graph, long int nv,
+				     igraph_vector_ptr_t *nattr);
+
+void igraph_haskell_attribute_permute_free(igraph_vector_ptr_t *v);
+
+int igraph_haskell_attribute_permute_vertices(const igraph_t *graph,
+					 igraph_t *newgraph,
+					 const igraph_vector_t *idx);
+
+int igraph_haskell_attribute_combine_vertices(const igraph_t *graph,
+			 igraph_t *newgraph,
+			 const igraph_vector_ptr_t *merges,
+			 const igraph_attribute_combination_t *comb);
+
+int igraph_haskell_attribute_add_edges(igraph_t *graph, const igraph_vector_t *edges,
+				 igraph_vector_ptr_t *nattr);
+
+int igraph_haskell_attribute_permute_edges(const igraph_t *graph,
+				      igraph_t *newgraph,
+				      const igraph_vector_t *idx);
+
+int igraph_haskell_attribute_combine_edges(const igraph_t *graph,
+			 igraph_t *newgraph,
+			 const igraph_vector_ptr_t *merges,
+			 const igraph_attribute_combination_t *comb);
+
+int igraph_haskell_attribute_get_info(const igraph_t *graph,
+				 igraph_strvector_t *gnames,
+				 igraph_vector_t *gtypes,
+				 igraph_strvector_t *vnames,
+				 igraph_vector_t *vtypes,
+				 igraph_strvector_t *enames,
+				 igraph_vector_t *etypes);
+
+igraph_bool_t igraph_haskell_attribute_has_attr(const igraph_t *graph,
+					 igraph_attribute_elemtype_t type,
+					 const char *name);
+
+int igraph_haskell_attribute_gettype(const igraph_t *graph,
+			      igraph_attribute_type_t *type,
+			      igraph_attribute_elemtype_t elemtype,
+			      const char *name);
+
+int igraph_haskell_attribute_get_numeric_graph_attr(const igraph_t *graph,
+					      const char *name,
+					      igraph_vector_t *value);
+
+int igraph_haskell_attribute_get_bool_graph_attr(const igraph_t *graph,
+					    const char *name,
+					    igraph_vector_bool_t *value);
+
+int igraph_haskell_attribute_get_string_graph_attr(const igraph_t *graph,
+					     const char *name,
+					     igraph_strvector_t *value_);
+
+int igraph_haskell_attribute_get_numeric_vertex_attr(const igraph_t *graph,
+					      const char *name,
+					      igraph_vs_t vs,
+					      igraph_vector_t *value);
+
+int igraph_haskell_attribute_get_bool_vertex_attr(const igraph_t *graph,
+					     const char *name,
+					     igraph_vs_t vs,
+					     igraph_vector_bool_t *value);
+
+int igraph_haskell_attribute_get_string_vertex_attr(const igraph_t *graph,
+					     const char *name,
+					     igraph_vs_t vs,
+					     igraph_strvector_t *value_);
+
+int igraph_haskell_attribute_get_numeric_edge_attr(const igraph_t *graph,
+					    const char *name,
+					    igraph_es_t es,
+					    igraph_vector_t *value);
+
+int igraph_haskell_attribute_get_string_edge_attr(const igraph_t *graph,
+					   const char *name,
+					   igraph_es_t es,
+					   igraph_strvector_t *value_);
+
+int igraph_haskell_attribute_get_bool_edge_attr(const igraph_t *graph,
+					   const char *name,
+					   igraph_es_t es,
+					   igraph_vector_bool_t *value);
+
+igraph_real_t igraph_haskell_attribute_GAN(const igraph_t *graph, const char *name);
+
+igraph_bool_t igraph_haskell_attribute_GAB(const igraph_t *graph, const char *name);
+
+const bytestring_t* igraph_haskell_attribute_GAS(const igraph_t *graph, const char *name);
+
+igraph_real_t igraph_haskell_attribute_VAN(const igraph_t *graph, const char *name,
+				      igraph_integer_t vid);
+
+igraph_bool_t igraph_haskell_attribute_VAB(const igraph_t *graph, const char *name,
+				    igraph_integer_t vid);
+
+const bytestring_t* igraph_haskell_attribute_VAS(const igraph_t *graph, const char *name,
+				    igraph_integer_t vid);
+
+igraph_real_t igraph_haskell_attribute_EAN(const igraph_t *graph, const char *name,
+				      igraph_integer_t eid);
+
+igraph_bool_t igraph_haskell_attribute_EAB(const igraph_t *graph, const char *name,
+				    igraph_integer_t eid);
+
+const bytestring_t* igraph_haskell_attribute_EAS(const igraph_t *graph, const char *name,
+				    igraph_integer_t eid);
+
+int igraph_haskell_attribute_VANV(const igraph_t *graph, const char *name,
+			   igraph_vs_t vids, igraph_vector_t *result);
+
+int igraph_haskell_attribute_VABV(const igraph_t *graph, const char *name,
+			   igraph_vs_t vids, igraph_vector_bool_t *result);
+
+int igraph_haskell_attribute_EANV(const igraph_t *graph, const char *name,
+			   igraph_es_t eids, igraph_vector_t *result);
+
+int igraph_haskell_attribute_EABV(const igraph_t *graph, const char *name,
+			   igraph_es_t eids, igraph_vector_bool_t *result);
+
+int igraph_haskell_attribute_VASV(const igraph_t *graph, const char *name,
+			   igraph_vs_t vids, igraph_strvector_t *result);
+
+int igraph_haskell_attribute_EASV(const igraph_t *graph, const char *name,
+			   igraph_es_t eids, igraph_strvector_t *result);
+
+int igraph_haskell_attribute_list(const igraph_t *graph,
+			   igraph_strvector_t *gnames, igraph_vector_t *gtypes,
+			   igraph_strvector_t *vnames, igraph_vector_t *vtypes,
+			   igraph_strvector_t *enames, igraph_vector_t *etypes);
+
+int igraph_haskell_attribute_GAN_set(igraph_t *graph, const char *name,
+			      igraph_real_t value);
+
+int igraph_haskell_attribute_GAB_set(igraph_t *graph, const char *name,
+			      igraph_bool_t value);
+
+int igraph_haskell_attribute_GAS_set(igraph_t *graph, const char *name,
+			      const bytestring_t *value);
+
+int igraph_haskell_attribute_VAN_set(igraph_t *graph, const char *name,
+			      igraph_integer_t vid, igraph_real_t value);
+
+int igraph_haskell_attribute_VAB_set(igraph_t *graph, const char *name,
+			      igraph_integer_t vid, igraph_bool_t value);
+
+int igraph_haskell_attribute_VAS_set(igraph_t *graph, const char *name,
+			      igraph_integer_t vid, const bytestring_t *value);
+
+int igraph_haskell_attribute_EAN_set(igraph_t *graph, const char *name,
+			      igraph_integer_t eid, igraph_real_t value);
+
+int igraph_haskell_attribute_EAB_set(igraph_t *graph, const char *name,
+			      igraph_integer_t eid, igraph_bool_t value);
+
+int igraph_haskell_attribute_EAS_set(igraph_t *graph, const char *name,
+			      igraph_integer_t eid, const bytestring_t *value);
+
+int igraph_haskell_attribute_VAN_setv(igraph_t *graph, const char *name,
+			       const igraph_vector_t *v);
+
+int igraph_haskell_attribute_VAB_setv(igraph_t *graph, const char *name,
+			       const igraph_vector_bool_t *v);
+
+int igraph_haskell_attribute_VAS_setv(igraph_t *graph, const char *name,
+			       const bsvector_t *sv);
+
+int igraph_haskell_attribute_EAN_setv(igraph_t *graph, const char *name,
+			       const igraph_vector_t *v);
+
+int igraph_haskell_attribute_EAB_setv(igraph_t *graph, const char *name,
+			       const igraph_vector_bool_t *v);
+
+int igraph_haskell_attribute_EAS_setv(igraph_t *graph, const char *name,
+			       const bsvector_t *sv);
+
+void igraph_haskell_attribute_free_rec(igraph_attribute_record_t *rec);
+
+void igraph_haskell_attribute_remove_g(igraph_t *graph, const char *name);
+
+void igraph_haskell_attribute_remove_v(igraph_t *graph, const char *name);
+
+void igraph_haskell_attribute_remove_e(igraph_t *graph, const char *name);
+
+void igraph_haskell_attribute_remove_all(igraph_t *graph, igraph_bool_t g,
+				  igraph_bool_t v, igraph_bool_t e);
+#endif
diff --git a/include/haskell_igraph.h b/include/haskell_igraph.h
new file mode 100644
--- /dev/null
+++ b/include/haskell_igraph.h
@@ -0,0 +1,8 @@
+#ifndef HASKELL_IGRAPH
+#define HASKELL_IGRAPH
+
+#include <igraph/igraph.h>
+
+void haskelligraph_init();
+
+#endif
diff --git a/src/IGraph.hs b/src/IGraph.hs
--- a/src/IGraph.hs
+++ b/src/IGraph.hs
@@ -1,12 +1,15 @@
 {-# LANGUAGE FlexibleContexts      #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 module IGraph
-    ( LGraph(..)
+    ( Graph(..)
+    , LGraph(..)
     , U(..)
     , D(..)
-    , Graph(..)
+    , decodeC
+    , empty
     , mkGraph
     , fromLabeledEdges
+    , fromLabeledEdges'
 
     , unsafeFreeze
     , freeze
@@ -26,108 +29,95 @@
     , emap
     ) where
 
+import           Conduit
 import           Control.Arrow             ((***))
-import           Control.Monad             (forM_, liftM)
+import           Control.Monad             (forM, forM_, liftM, replicateM,
+                                            unless)
 import           Control.Monad.Primitive
 import           Control.Monad.ST          (runST)
-import           Data.Binary
+import qualified Data.ByteString           as B
+import           Data.Conduit.Cereal
 import           Data.Hashable             (Hashable)
 import qualified Data.HashMap.Strict       as M
-import qualified Data.HashSet as S
+import qualified Data.HashSet              as S
+import           Data.List                 (sortBy)
 import           Data.Maybe
+import           Data.Ord                  (comparing)
+import           Data.Serialize
+import           Foreign                   (castPtr, with)
 import           System.IO.Unsafe          (unsafePerformIO)
 
 import           IGraph.Internal.Attribute
 import           IGraph.Internal.Constants
+import           IGraph.Internal.Data
 import           IGraph.Internal.Graph
 import           IGraph.Internal.Selector
 import           IGraph.Mutable
-
-type Node = Int
-type Edge = (Node, Node)
-
--- | graph with labeled nodes and edges
-data LGraph d v e = LGraph
-    { _graph       :: IGraphPtr
-    , _labelToNode :: M.HashMap v [Node]
-    }
-
-instance ( Binary v, Hashable v, Read v, Show v, Eq v
-         , Binary e, Read e, Show e, Graph d) => Binary (LGraph d v e) where
-    put gr = do
-        put nlabs
-        put es
-        put elabs
-      where
-        nlabs = map (nodeLab gr) $ nodes gr
-        es = edges gr
-        elabs = map (edgeLab gr) es
-    get = do
-        nlabs <- get
-        es <- get
-        elabs <- get
-        return $ mkGraph nlabs $ zip es elabs
+import           IGraph.Types
 
 class MGraph d => Graph d where
+    -- | Graph is directed or not.
     isDirected :: LGraph d v e -> Bool
     isD :: d -> Bool
 
+    -- | Return the number of nodes in a graph.
     nNodes :: LGraph d v e -> Int
-    nNodes (LGraph g _) = igraphVcount g
+    nNodes (LGraph g _) = unsafePerformIO $ igraphVcount g
     {-# INLINE nNodes #-}
 
-    nodes :: LGraph d v e -> [Int]
+    -- | Return all nodes. @nodes gr == [0 .. nNodes gr - 1]@.
+    nodes :: LGraph d v e -> [Node]
     nodes gr = [0 .. nNodes gr - 1]
     {-# INLINE nodes #-}
 
+    -- | Return the number of edges in a graph.
     nEdges :: LGraph d v e -> Int
-    nEdges (LGraph g _) = igraphEcount g
+    nEdges (LGraph g _) = unsafePerformIO $ igraphEcount g
     {-# INLINE nEdges #-}
 
+    -- | Return all edges.
     edges :: LGraph d v e -> [Edge]
     edges gr@(LGraph g _) = unsafePerformIO $ mapM (igraphEdge g) [0..n-1]
       where
         n = nEdges gr
     {-# INLINE edges #-}
 
+    -- | Whether a edge exists in the graph.
     hasEdge :: LGraph d v e -> Edge -> Bool
-    hasEdge (LGraph g _) (fr, to)
-        | igraphGetEid g fr to True False < 0 = False
-        | otherwise = True
+    hasEdge (LGraph g _) (fr, to) = unsafePerformIO $ do
+        i <- igraphGetEid g fr to True False
+        return $ i >= 0
     {-# INLINE hasEdge #-}
 
-    nodeLab :: Read v => LGraph d v e -> Node -> v
-    nodeLab (LGraph g _) i = read $ igraphCattributeVAS g vertexAttr i
+    -- | Return the label of given node.
+    nodeLab :: Serialize v => LGraph d v e -> Node -> v
+    nodeLab (LGraph g _) i = unsafePerformIO $
+        igraphHaskellAttributeVAS g vertexAttr i >>= fromBS
     {-# INLINE nodeLab #-}
 
-    nodeLabMaybe :: Read v => LGraph d v e -> Node -> Maybe v
-    nodeLabMaybe gr@(LGraph g _) i =
-        if igraphCattributeHasAttr g IgraphAttributeVertex vertexAttr
-            then Just $ nodeLab gr i
-            else Nothing
-    {-# INLINE nodeLabMaybe #-}
-
+    -- | Return all nodes that are associated with given label.
     getNodes :: (Hashable v, Eq v) => LGraph d v e -> v -> [Node]
     getNodes gr x = M.lookupDefault [] x $ _labelToNode gr
     {-# INLINE getNodes #-}
 
-    edgeLab :: Read e => LGraph d v e -> Edge -> e
-    edgeLab (LGraph g _) (fr,to) = read $ igraphCattributeEAS g edgeAttr $
-                                   igraphGetEid g fr to True True
+    -- | Return the label of given edge.
+    edgeLab :: Serialize e => LGraph d v e -> Edge -> e
+    edgeLab (LGraph g _) (fr,to) = unsafePerformIO $
+        igraphGetEid g fr to True True >>=
+            igraphHaskellAttributeEAS g edgeAttr >>= fromBS
     {-# INLINE edgeLab #-}
 
-    edgeLabMaybe :: Read e => LGraph d v e -> Edge -> Maybe e
-    edgeLabMaybe gr@(LGraph g _) i =
-        if igraphCattributeHasAttr g IgraphAttributeEdge edgeAttr
-            then Just $ edgeLab gr i
-            else Nothing
-    {-# INLINE edgeLabMaybe #-}
+    -- | Find the edge by edge ID.
+    getEdgeByEid :: LGraph d v e -> Int -> Edge
+    getEdgeByEid gr@(LGraph g _) i = unsafePerformIO $ igraphEdge g i
+    {-# INLINE getEdgeByEid #-}
 
-    edgeLabByEid :: Read e => LGraph d v e -> Int -> e
-    edgeLabByEid (LGraph g _) i = read $ igraphCattributeEAS g edgeAttr i
+    -- | Find the edge label by edge ID.
+    edgeLabByEid :: Serialize e => LGraph d v e -> Int -> e
+    edgeLabByEid (LGraph g _) i = unsafePerformIO $
+        igraphHaskellAttributeEAS g edgeAttr i >>= fromBS
     {-# INLINE edgeLabByEid #-}
 
-
 instance Graph U where
     isDirected = const False
     isD = const False
@@ -136,17 +126,60 @@
     isDirected = const True
     isD = const True
 
-mkGraph :: (Graph d, Hashable v, Read v, Eq v, Show v, Show e)
-        => [v] -> [(Edge, e)] -> LGraph d v e
+-- | Graph with labeled nodes and edges.
+data LGraph d v e = LGraph
+    { _graph       :: IGraph
+    , _labelToNode :: M.HashMap v [Node]
+    }
+
+instance (Graph d, Serialize v, Serialize e, Hashable v, Eq v)
+    => Serialize (LGraph d v e) where
+        put gr = do
+            put $ nNodes gr
+            go (nodeLab gr) (nNodes gr) 0
+            put $ nEdges gr
+            go (\i -> (getEdgeByEid gr i, edgeLabByEid gr i)) (nEdges gr) 0
+          where
+            go f n i | i >= n = return ()
+                     | otherwise = put (f i) >> go f n (i+1)
+        get = do
+            nn <- get
+            nds <- replicateM nn get
+            ne <- get
+            es <- replicateM ne get
+            return $ mkGraph nds es
+
+-- | Decode a graph from a stream of inputs. This may be more memory efficient
+-- than standard @decode@ function.
+decodeC :: ( PrimMonad m, MonadThrow m, Graph d
+           , Serialize v, Serialize e, Hashable v, Eq v )
+        => ConduitT B.ByteString o m (LGraph d v e)
+decodeC = do
+    nn <- sinkGet get
+    nds <- replicateM nn $ sinkGet get
+    ne <- sinkGet get
+    conduitGet2 get .| deserializeGraph nds ne
+
+-- | Create a empty graph.
+empty :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e)
+      => LGraph d v e
+empty = runST $ new 0 >>= unsafeFreeze
+
+-- | Create a graph.
+mkGraph :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e)
+        => [v]        -- ^ Nodes. Each will be assigned a ID from 0 to N.
+        -> [LEdge e]  -- ^ Labeled edges.
+        -> LGraph d v e
 mkGraph vattr es = runST $ do
     g <- new 0
-    addLNodes n vattr g
-    addLEdges (map (\((fr,to),x) -> (fr,to,x)) es) g
+    addLNodes vattr g
+    addLEdges es g
     unsafeFreeze g
   where
     n = length vattr
 
-fromLabeledEdges :: (Graph d, Hashable v, Read v, Eq v, Show v, Show e)
+-- | Create a graph from labeled edges.
+fromLabeledEdges :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e)
                  => [((v, v), e)] -> LGraph d v e
 fromLabeledEdges es = mkGraph labels es'
   where
@@ -155,25 +188,76 @@
     labels = S.toList $ S.fromList $ concat [ [a,b] | ((a,b),_) <- es ]
     labelToId = M.fromList $ zip labels [0..]
 
-unsafeFreeze :: (Hashable v, Eq v, Read v, PrimMonad m) => MLGraph (PrimState m) d v e -> m (LGraph d v e)
-unsafeFreeze (MLGraph g) = return $ LGraph g labToId
+-- | Create a graph from a stream of labeled edges.
+fromLabeledEdges' :: (PrimMonad m, Graph d, Hashable v, Serialize v, Eq v, Serialize e)
+                  => a    -- ^ Input, usually a file
+                  -> (a -> ConduitT () ((v, v), e) m ())  -- ^ deserialize the input into a stream of edges
+                  -> m (LGraph d v e)
+fromLabeledEdges' input mkConduit = do
+    (labelToId, _, ne) <- runConduit $ mkConduit input .|
+        foldlC f (M.empty, 0::Int, 0::Int)
+    let getId x = M.lookupDefault undefined x labelToId
+    runConduit $ mkConduit input .|
+        mapC (\((v1, v2), e) -> ((getId v1, getId v2), e)) .|
+        deserializeGraph (fst $ unzip $ sortBy (comparing snd) $ M.toList labelToId) ne
   where
-    labToId = M.fromListWith (++) $ zip labels $ map return [0..nV-1]
-    nV = igraphVcount g
-    labels = map (read . igraphCattributeVAS g vertexAttr) [0 .. nV-1]
+    f (vs, nn, ne) ((v1, v2), _) =
+        let (vs', nn') = add v1 $ add v2 (vs, nn)
+        in (vs', nn', ne+1)
+      where
+        add v (m, i) = if v `M.member` m
+            then (m, i)
+            else (M.insert v i m, i + 1)
 
-freeze :: (Hashable v, Eq v, Read v, PrimMonad m) => MLGraph (PrimState m) d v e -> m (LGraph d v e)
+deserializeGraph :: ( PrimMonad m, Graph d, Hashable v, Serialize v
+                    , Eq v, Serialize e )
+                 => [v]
+                 -> Int   -- ^ The number of edges
+                 -> ConduitT (LEdge e) o m (LGraph d v e)
+deserializeGraph nds ne = do
+    evec <- unsafePrimToPrim $ igraphVectorNew $ 2 * ne
+    bsvec <- unsafePrimToPrim $ bsvectorNew ne
+    let f i ((fr, to), attr) = unsafePrimToPrim $ do
+            igraphVectorSet evec (i*2) $ fromIntegral fr
+            igraphVectorSet evec (i*2+1) $ fromIntegral to
+            asBS attr $ \bs -> with bs $ \ptr -> bsvectorSet bsvec i $ castPtr ptr
+            return $ i + 1
+    foldMC f 0
+    gr@(MLGraph g) <- new 0
+    addLNodes nds gr
+    unsafePrimToPrim $ withEdgeAttr $ \eattr -> with (mkStrRec eattr bsvec) $ \ptr -> do
+            vptr <- fromPtrs [castPtr ptr]
+            withVectorPtr vptr (igraphAddEdges g evec . castPtr)
+    unsafeFreeze gr
+{-# INLINE deserializeGraph #-}
+
+-- | Convert a mutable graph to immutable graph.
+freeze :: (Hashable v, Eq v, Serialize v, PrimMonad m)
+       => MLGraph (PrimState m) d v e -> m (LGraph d v e)
 freeze (MLGraph g) = do
     g' <- unsafePrimToPrim $ igraphCopy g
     unsafeFreeze (MLGraph g')
 
-unsafeThaw :: PrimMonad m => LGraph d v e -> m (MLGraph (PrimState m) d v e)
-unsafeThaw (LGraph g _) = return $ MLGraph g
+-- | Convert a mutable graph to immutable graph. The original graph may not be
+-- used afterwards.
+unsafeFreeze :: (Hashable v, Eq v, Serialize v, PrimMonad m)
+             => MLGraph (PrimState m) d v e -> m (LGraph d v e)
+unsafeFreeze (MLGraph g) = unsafePrimToPrim $ do
+    nV <- igraphVcount g
+    labels <- forM [0 .. nV - 1] $ \i ->
+        igraphHaskellAttributeVAS g vertexAttr i >>= fromBS
+    return $ LGraph g $ M.fromListWith (++) $ zip labels $ map return [0..nV-1]
+  where
 
+-- | Create a mutable graph.
 thaw :: (PrimMonad m, Graph d) => LGraph d v e -> m (MLGraph (PrimState m) d v e)
 thaw (LGraph g _) = unsafePrimToPrim . liftM MLGraph . igraphCopy $ g
 
--- | Find all neighbors of the given node
+-- | Create a mutable graph. The original graph may not be used afterwards.
+unsafeThaw :: PrimMonad m => LGraph d v e -> m (MLGraph (PrimState m) d v e)
+unsafeThaw (LGraph g _) = return $ MLGraph g
+
+-- | Find all neighbors of the given node.
 neighbors :: LGraph d v e -> Node -> [Node]
 neighbors gr i = unsafePerformIO $ do
     vs <- igraphVsAdj i IgraphAll
@@ -195,7 +279,7 @@
     vitToList vit
 
 -- | Keep nodes that satisfy the constraint
-filterNodes :: (Hashable v, Eq v, Read v, Graph d)
+filterNodes :: (Hashable v, Eq v, Serialize v, Graph d)
             => (LGraph d v e -> Node -> Bool) -> LGraph d v e -> LGraph d v e
 filterNodes f gr = runST $ do
     let deleted = filter (not . f gr) $ nodes gr
@@ -204,7 +288,7 @@
     unsafeFreeze gr'
 
 -- | Apply a function to change nodes' labels.
-mapNodes :: (Graph d, Read v1, Show v2, Hashable v2, Eq v2, Read v2)
+mapNodes :: (Graph d, Serialize v1, Serialize v2, Hashable v2, Eq v2)
          => (Node -> v1 -> v2) -> LGraph d v1 e -> LGraph d v2 e
 mapNodes f gr = runST $ do
     (MLGraph gptr) <- thaw gr
@@ -213,7 +297,7 @@
     unsafeFreeze gr'
 
 -- | Apply a function to change edges' labels.
-mapEdges :: (Graph d, Read e1, Show e2, Hashable v, Eq v, Read v)
+mapEdges :: (Graph d, Serialize e1, Serialize e2, Hashable v, Eq v, Serialize v)
          => (Edge -> e1 -> e2) -> LGraph d v e1 -> LGraph d v e2
 mapEdges f gr = runST $ do
     (MLGraph gptr) <- thaw gr
@@ -223,9 +307,8 @@
         setEdgeAttr x (f e $ edgeLabByEid gr x) gr'
     unsafeFreeze gr'
 
-
--- | Keep nodes that satisfy the constraint
-filterEdges :: (Hashable v, Eq v, Read v, Graph d)
+-- | Keep nodes that satisfy the constraint.
+filterEdges :: (Hashable v, Eq v, Serialize v, Graph d)
             => (LGraph d v e -> Edge -> Bool) -> LGraph d v e -> LGraph d v e
 filterEdges f gr = runST $ do
     let deleted = filter (not . f gr) $ edges gr
@@ -233,23 +316,25 @@
     delEdges deleted gr'
     unsafeFreeze gr'
 
--- | Map a function over the node labels in a graph
-nmap :: (Graph d, Read v, Hashable u, Read u, Eq u, Show u)
+-- | Map a function over the node labels in a graph.
+nmap :: (Graph d, Serialize v, Hashable u, Serialize u, Eq u)
      => ((Node, v) -> u) -> LGraph d v e -> LGraph d u e
 nmap fn gr = unsafePerformIO $ do
     (MLGraph g) <- thaw gr
     forM_ (nodes gr) $ \i -> do
         let label = fn (i, nodeLab gr i)
-        igraphCattributeVASSet g vertexAttr i (show label)
+        asBS label $ \bs ->
+            with bs (igraphHaskellAttributeVASSet g vertexAttr i)
     unsafeFreeze (MLGraph g)
 
--- | Map a function over the edge labels in a graph
-emap :: (Graph d, Read v, Hashable v, Eq v, Read e1, Show e2)
+-- | Map a function over the edge labels in a graph.
+emap :: (Graph d, Serialize v, Hashable v, Eq v, Serialize e1, Serialize e2)
      => ((Edge, e1) -> e2) -> LGraph d v e1 -> LGraph d v e2
 emap fn gr = unsafePerformIO $ do
     (MLGraph g) <- thaw gr
     forM_ (edges gr) $ \(fr, to) -> do
+        i <- igraphGetEid g fr to True True
         let label = fn ((fr,to), edgeLabByEid gr i)
-            i = igraphGetEid g fr to True True
-        igraphCattributeEASSet g edgeAttr i (show label)
+        asBS label $ \bs ->
+            with bs (igraphHaskellAttributeEASSet g edgeAttr i)
     unsafeFreeze (MLGraph g)
diff --git a/src/IGraph/Clique.hs b/src/IGraph/Clique.hs
--- a/src/IGraph/Clique.hs
+++ b/src/IGraph/Clique.hs
@@ -17,7 +17,7 @@
 cliques gr (lo, hi) = unsafePerformIO $ do
     vpptr <- igraphVectorPtrNew 0
     _ <- igraphCliques (_graph gr) vpptr lo hi
-    (map.map) truncate <$> vectorPPtrToList vpptr
+    (map.map) truncate <$> toLists vpptr
 
 maximalCliques :: LGraph d v e
                -> (Int, Int)  -- ^ Minimum and maximum size of the cliques to be returned.
@@ -26,4 +26,4 @@
 maximalCliques gr (lo, hi) = unsafePerformIO $ do
     vpptr <- igraphVectorPtrNew 0
     _ <- igraphMaximalCliques (_graph gr) vpptr lo hi
-    (map.map) truncate <$> vectorPPtrToList vpptr
+    (map.map) truncate <$> toLists vpptr
diff --git a/src/IGraph/Community.hs b/src/IGraph/Community.hs
--- a/src/IGraph/Community.hs
+++ b/src/IGraph/Community.hs
@@ -4,32 +4,31 @@
     , findCommunity
     ) where
 
-import Control.Monad
-import Control.Applicative ((<$>))
-import Foreign
-import Foreign.C.Types
-import System.IO.Unsafe (unsafePerformIO)
-import Data.List
-import Data.Ord
-import Data.Function (on)
-import Data.Default.Class
+import           Control.Applicative       ((<$>))
+import           Control.Monad
+import           Data.Default.Class
+import           Data.Function             (on)
+import           Data.List
+import           Data.Ord
+import           Foreign
+import           Foreign.C.Types
+import           System.IO.Unsafe          (unsafePerformIO)
 
-import IGraph
-import IGraph.Mutable (U)
-import IGraph.Internal.Data
-import IGraph.Internal.Constants
-import IGraph.Internal.Community
-import IGraph.Internal.Arpack
+import           IGraph
+import           IGraph.Internal.Arpack
+import           IGraph.Internal.Community
+import           IGraph.Internal.Constants
+import           IGraph.Internal.Data
 
 data CommunityOpt = CommunityOpt
-    { _method :: CommunityMethod
-    , _weights :: Maybe [Double]
-    , _nIter :: Int  -- ^ [LeadingEigenvector] number of iterations, default is 10000
-    , _nSpins :: Int  -- ^ [Spinglass] number of spins, default is 25
+    { _method    :: CommunityMethod
+    , _weights   :: Maybe [Double]
+    , _nIter     :: Int  -- ^ [LeadingEigenvector] number of iterations, default is 10000
+    , _nSpins    :: Int  -- ^ [Spinglass] number of spins, default is 25
     , _startTemp :: Double  -- ^ [Spinglass] the temperature at the start
-    , _stopTemp :: Double  -- ^ [Spinglass] the algorithm stops at this temperature
-    , _coolFact :: Double  -- ^ [Spinglass] the cooling factor for the simulated annealing
-    , _gamma :: Double  -- ^ [Spinglass] the gamma parameter of the algorithm. 
+    , _stopTemp  :: Double  -- ^ [Spinglass] the algorithm stops at this temperature
+    , _coolFact  :: Double  -- ^ [Spinglass] the cooling factor for the simulated annealing
+    , _gamma     :: Double  -- ^ [Spinglass] the gamma parameter of the algorithm.
     }
 
 data CommunityMethod = LeadingEigenvector
@@ -51,8 +50,8 @@
 findCommunity gr opt = unsafePerformIO $ do
     result <- igraphVectorNew 0
     ws <- case _weights opt of
-        Just w -> listToVector w
-        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
+        Just w -> fromList w
+        _      -> liftM Vector $ newForeignPtr_ $ castPtr nullPtr
 
     case _method opt of
         LeadingEigenvector -> do
@@ -60,7 +59,7 @@
             igraphCommunityLeadingEigenvector (_graph gr) ws nullPtr result
                                               (_nIter opt) ap nullPtr False
                                               nullPtr nullPtr nullPtr
-                                              nullFunPtr nullPtr  
+                                              nullFunPtr nullPtr
         Spinglass ->
             igraphCommunitySpinglass (_graph gr) ws nullPtr nullPtr result
                                      nullPtr (_nSpins opt) False (_startTemp opt)
@@ -69,5 +68,4 @@
                                      IgraphSpincommImpOrig 1.0
 
     liftM ( map (fst . unzip) . groupBy ((==) `on` snd)
-          . sortBy (comparing snd) . zip [0..] ) $ vectorPtrToList result
-
+          . sortBy (comparing snd) . zip [0..] ) $ toList result
diff --git a/src/IGraph/Exporter/GEXF.hs b/src/IGraph/Exporter/GEXF.hs
--- a/src/IGraph/Exporter/GEXF.hs
+++ b/src/IGraph/Exporter/GEXF.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE DeriveGeneric     #-}
+{-# LANGUAGE FlexibleInstances #-}
 module IGraph.Exporter.GEXF
     ( NodeAttr(..)
     , defaultNodeAttributes
@@ -7,23 +9,35 @@
     , writeGEXF
     ) where
 
-import Data.Hashable
-import Data.Colour (AlphaColour, black, over, alphaChannel, opaque)
-import Data.Colour.SRGB (toSRGB24, channelRed, channelBlue, channelGreen)
-import Text.XML.HXT.Core
-import Data.Tree.NTree.TypeDefs
-import Text.XML.HXT.DOM.TypeDefs
-import IGraph
+import           Data.Colour               (AlphaColour, alphaChannel, black,
+                                            opaque, over)
+import           Data.Colour.SRGB          (channelBlue, channelGreen,
+                                            channelRed, toSRGB24)
+import           Data.Hashable
+import           Data.Serialize
+import           Data.Tree.NTree.TypeDefs
+import           GHC.Generics
+import           IGraph
+import           Text.XML.HXT.Core
+import           Text.XML.HXT.DOM.TypeDefs
 
+instance Serialize (AlphaColour Double) where
+    get = do
+        x <- get
+        return $ read x
+    put x = put $ show x
+
 data NodeAttr = NodeAttr
-    { _size :: Double
+    { _size       :: Double
     , _nodeColour :: AlphaColour Double
-    , _nodeLabel :: String
-    , _positionX :: Double
-    , _positionY :: Double
+    , _nodeLabel  :: String
+    , _positionX  :: Double
+    , _positionY  :: Double
     , _nodeZindex :: Int
-    } deriving (Show, Read, Eq)
+    } deriving (Show, Read, Eq, Generic)
 
+instance Serialize NodeAttr
+
 instance Hashable NodeAttr where
     hashWithSalt salt at = hashWithSalt salt $ _nodeLabel at
 
@@ -38,12 +52,14 @@
     }
 
 data EdgeAttr = EdgeAttr
-    { _edgeLabel :: String
-    , _edgeColour :: AlphaColour Double
-    , _edgeWeight :: Double
+    { _edgeLabel       :: String
+    , _edgeColour      :: AlphaColour Double
+    , _edgeWeight      :: Double
     , _edgeArrowLength :: Double
-    , _edgeZindex :: Int
-    } deriving (Show, Read, Eq)
+    , _edgeZindex      :: Int
+    } deriving (Show, Read, Eq, Generic)
+
+instance Serialize EdgeAttr
 
 instance Hashable EdgeAttr where
     hashWithSalt salt at = hashWithSalt salt $ _edgeLabel at
diff --git a/src/IGraph/Exporter/Graphics.hs b/src/IGraph/Exporter/Graphics.hs
--- a/src/IGraph/Exporter/Graphics.hs
+++ b/src/IGraph/Exporter/Graphics.hs
@@ -29,7 +29,7 @@
     drawEdge (from, to) = ( arrowBetween'
         ( with & arrowTail .~ noTail
                & arrowHead .~ arrowH
-               & headStyle %~ fc red
+               & headStyle %~ fcA (_edgeColour eattr)
                & headLength .~ output (_edgeArrowLength eattr)
         ) start end # lwO (_edgeWeight eattr) # lcA (_edgeColour eattr), _edgeZindex eattr )
       where
diff --git a/src/IGraph/Generators.hs b/src/IGraph/Generators.hs
--- a/src/IGraph/Generators.hs
+++ b/src/IGraph/Generators.hs
@@ -1,14 +1,21 @@
 module IGraph.Generators
     ( ErdosRenyiModel(..)
     , erdosRenyiGame
+    , degreeSequenceGame
+    , rewire
     ) where
 
-import IGraph
-import IGraph.Mutable
-import IGraph.Internal.Graph
-import IGraph.Internal.Constants
-import IGraph.Internal.Initialization
+import           Control.Monad                  (when)
+import           Data.Hashable                  (Hashable)
+import           Data.Serialize                 (Serialize)
 
+import           IGraph
+import           IGraph.Internal.Constants
+import           IGraph.Internal.Data
+import           IGraph.Internal.Graph
+import           IGraph.Internal.Initialization
+import           IGraph.Mutable
+
 data ErdosRenyiModel = GNP Int Double
                      | GNM Int Int
 
@@ -24,3 +31,24 @@
     gp <- igraphInit >> igraphErdosRenyiGame IgraphErdosRenyiGnm n
         (fromIntegral m) (isD d) self
     unsafeFreeze $ MLGraph gp
+
+-- | Generates a random graph with a given degree sequence.
+degreeSequenceGame :: [Int]   -- ^ Out degree
+                   -> [Int]   -- ^ In degree
+                   -> IO (LGraph D () ())
+degreeSequenceGame out_deg in_deg = do
+    out_deg' <- fromList $ map fromIntegral out_deg
+    in_deg' <- fromList $ map fromIntegral in_deg
+    gp <- igraphDegreeSequenceGame out_deg' in_deg' IgraphDegseqSimple
+    unsafeFreeze $ MLGraph gp
+
+-- | Randomly rewires a graph while preserving the degree distribution.
+rewire :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e)
+       => Int    -- ^ Number of rewiring trials to perform.
+       -> LGraph d v e
+       -> IO (LGraph d v e)
+rewire n gr = do
+    (MLGraph gptr) <- thaw gr
+    err <- igraphRewire gptr n IgraphRewiringSimple
+    when (err /= 0) $ error "failed to rewire graph!"
+    unsafeFreeze $ MLGraph gptr
diff --git a/src/IGraph/Internal/Arpack.chs b/src/IGraph/Internal/Arpack.chs
--- a/src/IGraph/Internal/Arpack.chs
+++ b/src/IGraph/Internal/Arpack.chs
@@ -5,8 +5,9 @@
 import Foreign
 import Foreign.C.Types
 
-#include "haskelligraph.h"
+#include "haskell_igraph.h"
 
-{#pointer *igraph_arpack_options_t as ArpackOptPtr foreign finalizer igraph_arpack_destroy newtype#}
+{#pointer *igraph_arpack_options_t as ArpackOpt foreign newtype#}
 
-{#fun igraph_arpack_new as ^ { } -> `ArpackOptPtr' #}
+{#fun igraph_arpack_options_init as igraphArpackNew
+    { + } -> `ArpackOpt' #}
diff --git a/src/IGraph/Internal/Attribute.chs b/src/IGraph/Internal/Attribute.chs
--- a/src/IGraph/Internal/Attribute.chs
+++ b/src/IGraph/Internal/Attribute.chs
@@ -1,9 +1,11 @@
 {-# LANGUAGE ForeignFunctionInterface #-}
 module IGraph.Internal.Attribute where
 
-import qualified Data.ByteString.Char8 as B
+import Data.ByteString (packCStringLen)
+import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
 import Control.Monad
 import Control.Applicative
+import Data.Serialize (Serialize, decode, encode)
 import Foreign
 import Foreign.C.Types
 import Foreign.C.String
@@ -14,18 +16,39 @@
 {#import IGraph.Internal.Constants #}
 
 #include "igraph/igraph.h"
+#include "haskell_attributes.h"
 
-makeAttributeRecord :: Show a
-                    => String    -- ^ name of the attribute
-                    -> [a]       -- ^ values of the attribute
-                    -> AttributeRecord
-makeAttributeRecord name xs = unsafePerformIO $ do
-    ptr <- newCAString name
-    value <- listToStrVector $ map (B.pack . show) xs
-    return $ AttributeRecord ptr 2 value
+-- The returned object will not be trackced by Haskell's GC. It should be freed
+-- by foreign codes.
+asBS :: Serialize a => a -> (BSLen -> IO b) -> IO b
+asBS x fn = unsafeUseAsCStringLen (encode x) (fn . BSLen)
+{-# INLINE asBS #-}
 
-data AttributeRecord = AttributeRecord CString Int StrVectorPtr
+asBSVector :: Serialize a => [a] -> (BSVector -> IO b) -> IO b
+asBSVector values fn = loop [] values
+  where
+    loop acc (x:xs) = unsafeUseAsCStringLen (encode x) $ \ptr ->
+        loop (BSLen ptr : acc) xs
+    loop acc _ = toBSVector (reverse acc) >>= fn
+{-# INLINE asBSVector #-}
 
+fromBS :: Serialize a => Ptr BSLen -> IO a
+fromBS ptr = do
+    BSLen x <- peek ptr
+    result <- decode <$> packCStringLen x
+    case result of
+        Left msg -> error msg
+        Right r -> return r
+{-# INLINE fromBS #-}
+
+mkStrRec :: CString    -- ^ name of the attribute
+         -> BSVector       -- ^ values of the attribute
+         -> AttributeRecord
+mkStrRec name xs = AttributeRecord name 2 xs
+{-# INLINE mkStrRec #-}
+
+data AttributeRecord = AttributeRecord CString Int BSVector
+
 instance Storable AttributeRecord where
     sizeOf _ = {#sizeof igraph_attribute_record_t #}
     alignment _ = {#alignof igraph_attribute_record_t #}
@@ -34,27 +57,27 @@
         <*> liftM fromIntegral ({#get igraph_attribute_record_t->type #} p)
         <*> ( do ptr <- {#get igraph_attribute_record_t->value #} p
                  fptr <- newForeignPtr_ . castPtr $ ptr
-                 return $ StrVectorPtr fptr )
+                 return $ BSVector fptr )
     poke p (AttributeRecord name t vptr) = do
         {#set igraph_attribute_record_t.name #} p name
         {#set igraph_attribute_record_t.type #} p $ fromIntegral t
-        withStrVectorPtr vptr $ \ptr ->
+        withBSVector vptr $ \ptr ->
             {#set igraph_attribute_record_t.value #} p $ castPtr ptr
 
-{#fun pure igraph_cattribute_has_attr as ^ { `IGraphPtr', `AttributeElemtype', `String' } -> `Bool' #}
+{#fun igraph_haskell_attribute_has_attr as ^ { `IGraph', `AttributeElemtype', `String' } -> `Bool' #}
 
-{#fun igraph_cattribute_GAN_set as ^ { `IGraphPtr', `String', `Double' } -> `Int' #}
+{#fun igraph_haskell_attribute_GAN_set as ^ { `IGraph', `String', `Double' } -> `Int' #}
 
-{#fun pure igraph_cattribute_GAN as ^ { `IGraphPtr', `String' } -> `Double' #}
+{#fun igraph_haskell_attribute_GAN as ^ { `IGraph', `String' } -> `Double' #}
 
-{#fun pure igraph_cattribute_VAS as ^ { `IGraphPtr', `String', `Int' } -> `String' #}
+{#fun igraph_haskell_attribute_VAS as ^ { `IGraph', `String', `Int' } -> `Ptr BSLen' castPtr #}
 
-{#fun pure igraph_cattribute_EAN as ^ { `IGraphPtr', `String', `Int' } -> `Double' #}
+{#fun igraph_haskell_attribute_EAN as ^ { `IGraph', `String', `Int' } -> `Double' #}
 
-{#fun pure igraph_cattribute_EAS as ^ { `IGraphPtr', `String', `Int' } -> `String' #}
+{#fun igraph_haskell_attribute_EAS as ^ { `IGraph', `String', `Int' } -> `Ptr BSLen' castPtr #}
 
-{#fun igraph_cattribute_EAS_setv as ^ { `IGraphPtr', `String', `StrVectorPtr' } -> `Int' #}
+{#fun igraph_haskell_attribute_EAS_setv as ^ { `IGraph', `String', `BSVector' } -> `Int' #}
 
-{#fun igraph_cattribute_VAS_set as ^ { `IGraphPtr', `String', `Int', `String' } -> `Int' #}
+{#fun igraph_haskell_attribute_VAS_set as ^ { `IGraph', `String', `Int', castPtr `Ptr BSLen' } -> `Int' #}
 
-{#fun igraph_cattribute_EAS_set as ^ { `IGraphPtr', `String', `Int', `String' } -> `Int' #}
+{#fun igraph_haskell_attribute_EAS_set as ^ { `IGraph', `String', `Int', castPtr `Ptr BSLen' } -> `Int' #}
diff --git a/src/IGraph/Internal/C2HS.hs b/src/IGraph/Internal/C2HS.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Internal/C2HS.hs
@@ -0,0 +1,73 @@
+module IGraph.Internal.C2HS (
+
+  -- * Conversion between C and Haskell types
+  cIntConv, cFloatConv, cToBool, cFromBool, cToEnum, cFromEnum,
+
+  -- * Composite marshalling functions
+  peekIntConv, peekFloatConv,
+
+) where
+
+-- system
+import Control.Monad
+import Foreign
+import Foreign.C
+
+
+-- Conversions -----------------------------------------------------------------
+--
+
+-- | Integral conversion
+--
+{-# INLINE cIntConv #-}
+cIntConv :: (Integral a, Integral b) => a -> b
+cIntConv  = fromIntegral
+
+-- | Floating conversion
+--
+{-# INLINE [1] cFloatConv #-}
+cFloatConv :: (RealFloat a, RealFloat b) => a -> b
+cFloatConv  = realToFrac
+-- As this conversion by default goes via `Rational', it can be very slow...
+{-# RULES
+  "cFloatConv/Float->Float"    forall (x::Float).  cFloatConv x = x;
+  "cFloatConv/Double->Double"  forall (x::Double). cFloatConv x = x;
+  "cFloatConv/Float->CFloat"   forall (x::Float).  cFloatConv x = CFloat x;
+  "cFloatConv/CFloat->Float"   forall (x::Float).  cFloatConv CFloat x = x;
+  "cFloatConv/Double->CDouble" forall (x::Double). cFloatConv x = CDouble x;
+  "cFloatConv/CDouble->Double" forall (x::Double). cFloatConv CDouble x = x
+ #-}
+
+-- | Obtain C value from Haskell 'Bool'.
+--
+{-# INLINE cFromBool #-}
+cFromBool :: Num a => Bool -> a
+cFromBool  = fromBool
+
+-- | Obtain Haskell 'Bool' from C value.
+--
+{-# INLINE cToBool #-}
+cToBool :: (Eq a, Num a) => a -> Bool
+cToBool  = toBool
+
+-- | Convert a C enumeration to Haskell.
+--
+{-# INLINE cToEnum #-}
+cToEnum :: (Integral i, Enum e) => i -> e
+cToEnum  = toEnum . cIntConv
+
+-- | Convert a Haskell enumeration to C.
+--
+{-# INLINE cFromEnum #-}
+cFromEnum :: (Enum e, Integral i) => e -> i
+cFromEnum  = cIntConv . fromEnum
+
+-- | Marshalling of numerals
+--
+{-# INLINE peekIntConv #-}
+peekIntConv :: (Storable a, Integral a, Integral b) => Ptr a -> IO b
+peekIntConv = liftM cIntConv . peek
+
+{-# INLINE peekFloatConv #-}
+peekFloatConv :: (Storable a, RealFloat a, RealFloat b) => Ptr a -> IO b
+peekFloatConv = liftM cFloatConv . peek
diff --git a/src/IGraph/Internal/Clique.chs b/src/IGraph/Internal/Clique.chs
--- a/src/IGraph/Internal/Clique.chs
+++ b/src/IGraph/Internal/Clique.chs
@@ -10,8 +10,8 @@
 {#import IGraph.Internal.Graph #}
 {#import IGraph.Internal.Data #}
 
-#include "igraph/igraph.h"
+#include "haskell_igraph.h"
 
-{#fun igraph_cliques as ^ { `IGraphPtr', `VectorPPtr', `Int', `Int' } -> `Int' #}
+{#fun igraph_cliques as ^ { `IGraph', `VectorPtr', `Int', `Int' } -> `Int' #}
 
-{#fun igraph_maximal_cliques as ^ { `IGraphPtr', `VectorPPtr', `Int', `Int' } -> `Int' #}
+{#fun igraph_maximal_cliques as ^ { `IGraph', `VectorPtr', `Int', `Int' } -> `Int' #}
diff --git a/src/IGraph/Internal/Community.chs b/src/IGraph/Internal/Community.chs
--- a/src/IGraph/Internal/Community.chs
+++ b/src/IGraph/Internal/Community.chs
@@ -9,15 +9,15 @@
 {#import IGraph.Internal.Data #}
 {#import IGraph.Internal.Constants #}
 
-#include "igraph/igraph.h"
+#include "haskell_igraph.h"
 
 {#fun igraph_community_spinglass as ^
-{ `IGraphPtr'
-, `VectorPtr'
+{ `IGraph'
+, `Vector'
 , id `Ptr CDouble'
 , id `Ptr CDouble'
-, `VectorPtr'
-, id `Ptr VectorPtr'
+, `Vector'
+, id `Ptr Vector'
 , `Int'
 , `Bool'
 , `Double'
@@ -30,25 +30,25 @@
 } -> `Int' #}
 
 {#fun igraph_community_leading_eigenvector as ^
-{ `IGraphPtr'
-, `VectorPtr'
-, id `Ptr MatrixPtr'
-, `VectorPtr'
+{ `IGraph'
+, `Vector'
+, id `Ptr Matrix'
+, `Vector'
 , `Int'
-, `ArpackOptPtr'
+, `ArpackOpt'
 , id `Ptr CDouble'
 , `Bool'
-, id `Ptr VectorPtr'
-, id `Ptr VectorPPtr'
+, id `Ptr Vector'
 , id `Ptr VectorPtr'
+, id `Ptr Vector'
 , id `T'
 , id `Ptr ()'
 } -> `Int' #}
 
-type T = FunPtr ( Ptr VectorPtr
+type T = FunPtr ( Ptr Vector
                 -> CLong
                 -> CDouble
-                -> Ptr VectorPtr
+                -> Ptr Vector
                 -> FunPtr (Ptr CDouble -> Ptr CDouble -> CInt -> Ptr () -> IO CInt)
                 -> Ptr ()
                 -> Ptr ()
diff --git a/src/IGraph/Internal/Constants.chs b/src/IGraph/Internal/Constants.chs
--- a/src/IGraph/Internal/Constants.chs
+++ b/src/IGraph/Internal/Constants.chs
@@ -28,3 +28,9 @@
 
 {#enum igraph_erdos_renyi_t as ErdosRenyi {underscoreToCase}
     deriving (Show, Read, Eq) #}
+
+{#enum igraph_rewiring_t as Rewiring {underscoreToCase}
+    deriving (Show, Read, Eq) #}
+
+{#enum igraph_degseq_t as Degseq {underscoreToCase}
+    deriving (Show, Read, Eq) #}
diff --git a/src/IGraph/Internal/Data.chs b/src/IGraph/Internal/Data.chs
--- a/src/IGraph/Internal/Data.chs
+++ b/src/IGraph/Internal/Data.chs
@@ -1,6 +1,52 @@
 {-# LANGUAGE ForeignFunctionInterface #-}
-module IGraph.Internal.Data where
+module IGraph.Internal.Data
+    ( Vector(..)
+    , withVector
+    , igraphVectorNew
+    , fromList
+    , toList
+    , igraphVectorNull
+    , igraphVectorFill
+    , igraphVectorE
+    , igraphVectorSet
+    , igraphVectorTail
+    , igraphVectorSize
+    , igraphVectorCopyTo
 
+    , VectorPtr(..)
+    , withVectorPtr
+    , igraphVectorPtrNew
+    , fromPtrs
+    , toLists
+
+    , StrVector(..)
+    , withStrVector
+    , igraphStrvectorNew
+    , igraphStrvectorGet
+    , toStrVector
+
+    , BSLen(..)
+    , BSVector(..)
+    , withBSVector
+    , bsvectorNew
+    , bsvectorSet
+    , toBSVector
+
+    , Matrix(..)
+    , withMatrix
+    , igraphMatrixNew
+    , igraphMatrixNull
+    , igraphMatrixFill
+    , igraphMatrixE
+    , igraphMatrixSet
+    , igraphMatrixCopyTo
+    , igraphMatrixNrow
+    , igraphMatrixNcol
+    , fromRowLists
+    , toRowLists
+    , toColumnLists
+    ) where
+
 import Control.Monad
 import qualified Data.ByteString.Char8 as B
 import Foreign
@@ -10,94 +56,109 @@
 import Data.List (transpose)
 import Data.List.Split (chunksOf)
 
-#include "haskelligraph.h"
+#include "haskell_igraph.h"
+#include "bytestring.h"
 
-{#pointer *igraph_vector_t as VectorPtr foreign finalizer igraph_vector_destroy newtype#}
+--------------------------------------------------------------------------------
+-- Igraph vector
+--------------------------------------------------------------------------------
 
+{#pointer *igraph_vector_t as Vector foreign finalizer
+    igraph_vector_destroy newtype#}
+
 -- Construtors and destructors
 
-{#fun igraph_vector_init as igraphVectorNew { +, `Int' } -> `VectorPtr' #}
+{#fun igraph_vector_init as igraphVectorNew { +, `Int' } -> `Vector' #}
 
-listToVector :: [Double] -> IO VectorPtr
-listToVector xs = do
-    vec <- igraphVectorNew n
-    forM_ (zip [0..] xs) $ \(i,x) -> igraphVectorSet vec i x
-    return vec
-  where
-    n = length xs
+{#fun igraph_vector_init_copy as ^ { +, id `Ptr CDouble', `Int' } -> `Vector' #}
 
-vectorPtrToList :: VectorPtr -> IO [Double]
-vectorPtrToList vptr = do
-    n <- igraphVectorSize vptr
+fromList :: [Double] -> IO Vector
+fromList xs = withArrayLen (map realToFrac xs) $ \n ptr ->
+    igraphVectorInitCopy ptr n
+{-# INLINE fromList #-}
+
+toList :: Vector -> IO [Double]
+toList vec = do
+    n <- igraphVectorSize vec
     allocaArray n $ \ptr -> do
-        igraphVectorCopyTo vptr ptr
+        igraphVectorCopyTo vec ptr
         liftM (map realToFrac) $ peekArray n ptr
+{-# INLINE toList #-}
 
 -- Initializing elements
 
-{#fun igraph_vector_null as ^ { `VectorPtr' } -> `()' #}
+{#fun igraph_vector_null as ^ { `Vector' } -> `()' #}
 
-{#fun igraph_vector_fill as ^ { `VectorPtr', `Double' } -> `()' #}
+{#fun igraph_vector_fill as ^ { `Vector', `Double' } -> `()' #}
 
 
 -- Accessing elements
 
-{#fun pure igraph_vector_e as ^ { `VectorPtr', `Int' } -> `Double' #}
+{#fun pure igraph_vector_e as ^ { `Vector', `Int' } -> `Double' #}
 
-{#fun igraph_vector_set as ^ { `VectorPtr', `Int', `Double' } -> `()' #}
+{#fun igraph_vector_set as ^ { `Vector', `Int', `Double' } -> `()' #}
 
-{#fun pure igraph_vector_tail as ^ { `VectorPtr' } -> `Double' #}
+{#fun pure igraph_vector_tail as ^ { `Vector' } -> `Double' #}
 
 
 -- Copying vectors
 
-{#fun igraph_vector_copy_to as ^ { `VectorPtr', id `Ptr CDouble' } -> `()' #}
+{#fun igraph_vector_copy_to as ^ { `Vector', id `Ptr CDouble' } -> `()' #}
 
 -- Vector properties
-{#fun igraph_vector_size as ^ { `VectorPtr' } -> `Int' #}
+{#fun igraph_vector_size as ^ { `Vector' } -> `Int' #}
 
 
-{#pointer *igraph_vector_ptr_t as VectorPPtr foreign finalizer igraph_vector_ptr_destroy_all newtype#}
+{#pointer *igraph_vector_ptr_t as VectorPtr foreign finalizer
+    igraph_vector_ptr_destroy_all newtype#}
 
-{#fun igraph_vector_ptr_init as igraphVectorPtrNew { +, `Int' } -> `VectorPPtr' #}
+{#fun igraph_vector_ptr_init as igraphVectorPtrNew { +, `Int' } -> `VectorPtr' #}
 
-{#fun igraph_vector_ptr_e as ^ { `VectorPPtr', `Int' } -> `Ptr ()' #}
-{#fun igraph_vector_ptr_set as ^ { `VectorPPtr', `Int', id `Ptr ()' } -> `()' #}
-{#fun igraph_vector_ptr_size as ^ { `VectorPPtr' } -> `Int' #}
+{#fun igraph_vector_ptr_e as ^ { `VectorPtr', `Int' } -> `Ptr ()' #}
+{#fun igraph_vector_ptr_set as ^ { `VectorPtr', `Int', id `Ptr ()' } -> `()' #}
+{#fun igraph_vector_ptr_size as ^ { `VectorPtr' } -> `Int' #}
 
-listToVectorP :: [Ptr ()] -> IO VectorPPtr
-listToVectorP xs = do
+fromPtrs :: [Ptr ()] -> IO VectorPtr
+fromPtrs xs = do
     vptr <- igraphVectorPtrNew n
     forM_ (zip [0..] xs) $ \(i,x) -> igraphVectorPtrSet vptr i x
     return vptr
   where
     n = length xs
+{-# INLINE fromPtrs #-}
 
-vectorPPtrToList :: VectorPPtr -> IO [[Double]]
-vectorPPtrToList vpptr = do
+toLists :: VectorPtr -> IO [[Double]]
+toLists vpptr = do
     n <- igraphVectorPtrSize vpptr
     forM [0..n-1] $ \i -> do
         vptr <- igraphVectorPtrE vpptr i
-        fptr <- newForeignPtr_ $ castPtr vptr
-        vectorPtrToList $ VectorPtr fptr
+        vec <- newForeignPtr_ $ castPtr vptr
+        toList $ Vector vec
+{-# INLINE toLists #-}
 
+--------------------------------------------------------------------------------
+-- Igraph string vector
+--------------------------------------------------------------------------------
 
-{#pointer *igraph_strvector_t as StrVectorPtr foreign finalizer igraph_strvector_destroy newtype#}
+{#pointer *igraph_strvector_t as StrVector foreign finalizer igraph_strvector_destroy newtype#}
 
-{#fun igraph_strvector_init as igraphStrvectorNew { +, `Int' } -> `StrVectorPtr' #}
+{#fun igraph_strvector_init as igraphStrvectorNew { +, `Int' } -> `StrVector' #}
 
-{#fun igraph_strvector_get_ as igraphStrvectorGet' { `StrVectorPtr', `Int' } -> `Ptr CString' id #}
+{#fun igraph_strvector_get as ^
+    { `StrVector'
+    , `Int'
+    , alloca- `String' peekString*
+    } -> `CInt' void-#}
 
-igraphStrvectorGet :: StrVectorPtr -> Int -> String
-igraphStrvectorGet vec i = unsafePerformIO $ do
-    ptr <- igraphStrvectorGet' vec i
-    peek ptr >>= peekCString
+peekString :: Ptr CString -> IO String
+peekString ptr = peek ptr >>= peekCString
+{-# INLINE peekString #-}
 
-{#fun igraph_strvector_set as ^ { `StrVectorPtr', `Int', id `CString'} -> `()' #}
-{#fun igraph_strvector_set2 as ^ { `StrVectorPtr', `Int', id `CString', `Int'} -> `()' #}
+{#fun igraph_strvector_set as ^ { `StrVector', `Int', id `CString'} -> `()' #}
+{#fun igraph_strvector_set2 as ^ { `StrVector', `Int', id `CString', `Int'} -> `()' #}
 
-listToStrVector :: [B.ByteString] -> IO StrVectorPtr
-listToStrVector xs = do
+toStrVector :: [B.ByteString] -> IO StrVector
+toStrVector xs = do
     vec <- igraphStrvectorNew n
     forM_ (zip [0..] xs) $ \(i,x) -> B.useAsCString x (igraphStrvectorSet vec i)
     return vec
@@ -105,42 +166,84 @@
     n = length xs
 
 
-{#pointer *igraph_matrix_t as MatrixPtr foreign finalizer igraph_matrix_destroy newtype#}
+--------------------------------------------------------------------------------
+-- Customized string vector
+--------------------------------------------------------------------------------
 
-{#fun igraph_matrix_init as igraphMatrixNew { +, `Int', `Int' } -> `MatrixPtr' #}
+newtype BSLen = BSLen CStringLen
 
-{#fun igraph_matrix_null as ^ { `MatrixPtr' } -> `()' #}
+instance Storable BSLen where
+    sizeOf _ = {#sizeof bytestring_t #}
+    alignment _ = {#alignof bytestring_t #}
+    peek p = do
+        n <- ({#get bytestring_t->len #} p)
+        ptr <- {#get bytestring_t->value #} p
+        return $ BSLen (ptr, fromIntegral n)
+    poke p (BSLen (ptr, n)) = {#set bytestring_t.len #} p (fromIntegral n) >>
+        {#set bytestring_t.value #} p ptr
 
-{#fun igraph_matrix_fill as ^ { `MatrixPtr', `Double' } -> `()' #}
+{#pointer *bsvector_t as BSVector foreign finalizer bsvector_destroy newtype#}
 
-{#fun igraph_matrix_e as ^ { `MatrixPtr', `Int', `Int' } -> `Double' #}
+{#fun bsvector_init as bsvectorNew { +, `Int' } -> `BSVector' #}
 
-{#fun igraph_matrix_set as ^ { `MatrixPtr', `Int', `Int', `Double' } -> `()' #}
+--{#fun bsvector_get as bsVectorGet { `BSVectorPtr', `Int', + } -> `Ptr (Ptr BSLen)' id #}
 
-{#fun igraph_matrix_copy_to as ^ { `MatrixPtr', id `Ptr CDouble' } -> `()' #}
+{-
+bsVectorGet :: BSVectorPtr -> Int -> BSLen
+bsVectorGet vec i = unsafePerformIO $ do
+    ptrptr <- bsVectorGet vec i
+    peek ptrptr >>= peek
+    -}
 
-{#fun igraph_matrix_nrow as ^ { `MatrixPtr' } -> `Int' #}
+{#fun bsvector_set as ^ { `BSVector', `Int', `Ptr ()'} -> `()' #}
 
-{#fun igraph_matrix_ncol as ^ { `MatrixPtr' } -> `Int' #}
+toBSVector :: [BSLen] -> IO BSVector
+toBSVector xs = do
+    vec <- bsvectorNew n
+    forM_ (zip [0..] xs) $ \(i, x) -> with x $ \ptr -> bsvectorSet vec i $ castPtr ptr
+    return vec
+  where
+    n = length xs
 
+
+{#pointer *igraph_matrix_t as Matrix foreign finalizer igraph_matrix_destroy newtype#}
+
+{#fun igraph_matrix_init as igraphMatrixNew { +, `Int', `Int' } -> `Matrix' #}
+
+{#fun igraph_matrix_null as ^ { `Matrix' } -> `()' #}
+
+{#fun igraph_matrix_fill as ^ { `Matrix', `Double' } -> `()' #}
+
+{#fun igraph_matrix_e as ^ { `Matrix', `Int', `Int' } -> `Double' #}
+
+{#fun igraph_matrix_set as ^ { `Matrix', `Int', `Int', `Double' } -> `()' #}
+
+{#fun igraph_matrix_copy_to as ^ { `Matrix', id `Ptr CDouble' } -> `()' #}
+
+{#fun igraph_matrix_nrow as ^ { `Matrix' } -> `Int' #}
+
+{#fun igraph_matrix_ncol as ^ { `Matrix' } -> `Int' #}
+
 -- row lists to matrix
-listsToMatrixPtr :: [[Double]] -> IO MatrixPtr
-listsToMatrixPtr xs = do
-    mptr <- igraphMatrixNew r c
-    forM_ (zip [0..] xs) $ \(i, row) ->
-        forM_ (zip [0..] row) $ \(j,v) ->
-            igraphMatrixSet mptr i j v
-    return mptr
+fromRowLists :: [[Double]] -> IO Matrix
+fromRowLists xs
+    | all (==c) $ map length xs = do
+        mptr <- igraphMatrixNew r c
+        forM_ (zip [0..] xs) $ \(i, row) ->
+            forM_ (zip [0..] row) $ \(j,v) ->
+                igraphMatrixSet mptr i j v
+        return mptr
+    | otherwise = error "Not a matrix."
   where
     r = length xs
-    c = maximum $ map length xs
+    c = length $ head xs
 
 -- to row lists
-matrixPtrToLists :: MatrixPtr -> IO [[Double]]
-matrixPtrToLists = liftM transpose . matrixPtrToColumnLists
+toRowLists :: Matrix -> IO [[Double]]
+toRowLists = liftM transpose . toColumnLists
 
-matrixPtrToColumnLists :: MatrixPtr -> IO [[Double]]
-matrixPtrToColumnLists mptr = do
+toColumnLists :: Matrix -> IO [[Double]]
+toColumnLists mptr = do
     r <- igraphMatrixNrow mptr
     c <- igraphMatrixNcol mptr
     xs <- allocaArray (r*c) $ \ptr -> do
diff --git a/src/IGraph/Internal/Graph.chs b/src/IGraph/Internal/Graph.chs
--- a/src/IGraph/Internal/Graph.chs
+++ b/src/IGraph/Internal/Graph.chs
@@ -6,54 +6,80 @@
 import Foreign.C.Types
 import System.IO.Unsafe (unsafePerformIO)
 
+import IGraph.Internal.C2HS
 {#import IGraph.Internal.Initialization #}
 {#import IGraph.Internal.Data #}
 {#import IGraph.Internal.Constants #}
 
-#include "haskelligraph.h"
+#include "haskell_igraph.h"
 
-{#pointer *igraph_t as IGraphPtr foreign finalizer igraph_destroy newtype#}
+--------------------------------------------------------------------------------
+-- Graph Constructors and Destructors
+--------------------------------------------------------------------------------
 
--- | create a igraph object and attach a finalizer
-igraphNew :: Int -> Bool -> HasInit -> IO IGraphPtr
-igraphNew n directed _ = igraphNew' n directed
+{#pointer *igraph_t as IGraph foreign finalizer igraph_destroy newtype#}
 
--- Graph Constructors and Destructors
+{#fun igraph_empty as igraphNew' { +, `Int', `Bool' } -> `IGraph' #}
 
-{#fun igraph_empty as igraphNew' { +, `Int', `Bool' } -> `IGraphPtr' #}
+{#fun igraph_copy as ^ { +, `IGraph' } -> `IGraph' #}
 
-{#fun igraph_copy as ^ { +, `IGraphPtr' } -> `IGraphPtr' #}
+-- | Create a igraph object and attach a finalizer
+igraphNew :: Int -> Bool -> HasInit -> IO IGraph
+igraphNew n directed _ = igraphNew' n directed
 
+--------------------------------------------------------------------------------
 -- Basic Query Operations
+--------------------------------------------------------------------------------
 
-{#fun pure igraph_vcount as ^ { `IGraphPtr' } -> `Int' #}
+{#fun igraph_vcount as ^ { `IGraph' } -> `Int' #}
 
-{#fun pure igraph_ecount as ^ { `IGraphPtr' } -> `Int' #}
+{#fun igraph_ecount as ^ { `IGraph' } -> `Int' #}
 
-{#fun pure igraph_get_eid_ as igraphGetEid { `IGraphPtr', `Int', `Int', `Bool', `Bool' } -> `Int' #}
+{#fun igraph_get_eid as ^
+    { `IGraph'
+    , alloca- `Int' peekIntConv*
+    , `Int'
+    , `Int'
+    , `Bool'
+    , `Bool'
+    } -> `CInt' void-#}
 
-{#fun igraph_edge as igraphEdge' { `IGraphPtr', `Int', id `Ptr CInt', id `Ptr CInt' } -> `Int' #}
-igraphEdge :: IGraphPtr -> Int -> IO (Int, Int)
-igraphEdge g i = alloca $ \fr -> alloca $ \to -> do
-    igraphEdge' g i fr to
-    fr' <- peek fr
-    to' <- peek to
-    return (fromIntegral fr', fromIntegral to')
+{#fun igraph_edge as ^
+    { `IGraph'
+    , `Int'
+    , alloca- `Int' peekIntConv*
+    , alloca- `Int' peekIntConv*
+    } -> `CInt' void-#}
 
 -- Adding and Deleting Vertices and Edges
 
-{# fun igraph_add_vertices as ^ { `IGraphPtr', `Int', id `Ptr ()' } -> `()' #}
+{# fun igraph_add_vertices as ^ { `IGraph', `Int', id `Ptr ()' } -> `()' #}
 
-{# fun igraph_add_edge as ^ { `IGraphPtr', `Int', `Int' } -> `()' #}
+{# fun igraph_add_edge as ^ { `IGraph', `Int', `Int' } -> `()' #}
 
-{# fun igraph_add_edges as ^ { `IGraphPtr', `VectorPtr', id `Ptr ()' } -> `()' #}
+-- | The edges are given in a vector, the first two elements define the first
+-- edge (the order is from , to for directed graphs). The vector should
+-- contain even number of integer numbers between zero and the number of
+-- vertices in the graph minus one (inclusive). If you also want to add
+-- new vertices, call igraph_add_vertices() first.
+{# fun igraph_add_edges as ^
+    { `IGraph'     -- ^ The graph to which the edges will be added.
+    , `Vector'     -- ^ The edges themselves.
+    , id `Ptr ()'  -- ^ The attributes of the new edges.
+    } -> `()' #}
 
 
 -- generators
 
-{#fun igraph_full as ^ { +, `Int', `Bool', `Bool' } -> `IGraphPtr' #}
+{#fun igraph_full as ^ { +, `Int', `Bool', `Bool' } -> `IGraph' #}
 
 {#fun igraph_erdos_renyi_game as ^ { +, `ErdosRenyi', `Int', `Double', `Bool'
-    , `Bool'} -> `IGraphPtr' #}
-    
-{#fun igraph_isoclass_create as ^ { +, `Int', `Int', `Bool' } -> `IGraphPtr' #}
+    , `Bool' } -> `IGraph' #}
+
+{#fun igraph_degree_sequence_game as ^ { +, `Vector', `Vector'
+    , `Degseq' } -> `IGraph' #}
+
+{#fun igraph_rewire as ^ { `IGraph', `Int', `Rewiring' } -> `Int' #}
+
+
+{#fun igraph_isoclass_create as ^ { +, `Int', `Int', `Bool' } -> `IGraph' #}
diff --git a/src/IGraph/Internal/Initialization.chs b/src/IGraph/Internal/Initialization.chs
--- a/src/IGraph/Internal/Initialization.chs
+++ b/src/IGraph/Internal/Initialization.chs
@@ -1,7 +1,7 @@
 {-# LANGUAGE ForeignFunctionInterface #-}
 module IGraph.Internal.Initialization where
 
-#include "haskelligraph.h"
+#include "haskell_igraph.h"
 
 data HasInit
 
diff --git a/src/IGraph/Internal/Isomorphism.chs b/src/IGraph/Internal/Isomorphism.chs
--- a/src/IGraph/Internal/Isomorphism.chs
+++ b/src/IGraph/Internal/Isomorphism.chs
@@ -7,12 +7,12 @@
 {#import IGraph.Internal.Graph #}
 {#import IGraph.Internal.Data #}
 
-#include "igraph/igraph.h"
+#include "haskell_igraph.h"
 
-{#fun igraph_get_subisomorphisms_vf2 as ^ { `IGraphPtr', `IGraphPtr',
-    id `Ptr ()', id `Ptr ()', id `Ptr ()', id `Ptr ()', `VectorPPtr',
-    id `FunPtr (Ptr IGraphPtr -> Ptr IGraphPtr -> CInt -> CInt -> Ptr () -> IO CInt)',
-    id `FunPtr (Ptr IGraphPtr -> Ptr IGraphPtr -> CInt -> CInt -> Ptr () -> IO CInt)',
+{#fun igraph_get_subisomorphisms_vf2 as ^ { `IGraph', `IGraph',
+    id `Ptr ()', id `Ptr ()', id `Ptr ()', id `Ptr ()', `VectorPtr',
+    id `FunPtr (Ptr IGraph -> Ptr IGraph -> CInt -> CInt -> Ptr () -> IO CInt)',
+    id `FunPtr (Ptr IGraph -> Ptr IGraph -> CInt -> CInt -> Ptr () -> IO CInt)',
     id `Ptr ()'} -> `Int' #}
 
-{#fun igraph_isomorphic as ^ { `IGraphPtr', `IGraphPtr', id `Ptr CInt' } -> `Int' #}
+{#fun igraph_isomorphic as ^ { `IGraph', `IGraph', id `Ptr CInt' } -> `Int' #}
diff --git a/src/IGraph/Internal/Layout.chs b/src/IGraph/Internal/Layout.chs
--- a/src/IGraph/Internal/Layout.chs
+++ b/src/IGraph/Internal/Layout.chs
@@ -12,22 +12,22 @@
 
 #include "igraph/igraph.h"
 
-{#fun igraph_layout_kamada_kawai as ^ { `IGraphPtr'
-, `MatrixPtr'
+{#fun igraph_layout_kamada_kawai as ^ { `IGraph'
+, `Matrix'
 , `Int'
 , `Double'
 , `Double'
 , `Double'
 , `Double'
 , `Bool'
-, id `Ptr VectorPtr'
-, id `Ptr VectorPtr'
-, id `Ptr VectorPtr'
-, id `Ptr VectorPtr'
+, id `Ptr Vector'
+, id `Ptr Vector'
+, id `Ptr Vector'
+, id `Ptr Vector'
 } -> `Int' #}
 
-{# fun igraph_layout_lgl as ^ { `IGraphPtr'
-, `MatrixPtr'
+{# fun igraph_layout_lgl as ^ { `IGraph'
+, `Matrix'
 , `Int'
 , `Double'
 , `Double'
diff --git a/src/IGraph/Internal/Motif.chs b/src/IGraph/Internal/Motif.chs
--- a/src/IGraph/Internal/Motif.chs
+++ b/src/IGraph/Internal/Motif.chs
@@ -11,10 +11,10 @@
 {#import IGraph.Internal.Constants #}
 {#import IGraph.Internal.Data #}
 
-#include "igraph/igraph.h"
+#include "haskell_igraph.h"
 
-{#fun igraph_triad_census as ^ { `IGraphPtr'
-                               , `VectorPtr' } -> `Int' #}
+{#fun igraph_triad_census as ^ { `IGraph'
+                               , `Vector' } -> `Int' #}
 
-{#fun igraph_motifs_randesu as ^ { `IGraphPtr', `VectorPtr', `Int'
-                                 , `VectorPtr' } -> `Int' #}
+{#fun igraph_motifs_randesu as ^ { `IGraph', `Vector', `Int'
+                                 , `Vector' } -> `Int' #}
diff --git a/src/IGraph/Internal/Selector.chs b/src/IGraph/Internal/Selector.chs
--- a/src/IGraph/Internal/Selector.chs
+++ b/src/IGraph/Internal/Selector.chs
@@ -10,28 +10,22 @@
 {#import IGraph.Internal.Graph #}
 {#import IGraph.Internal.Data #}
 
-#include "igraph/igraph.h"
+#include "haskell_igraph.h"
 
-{#pointer *igraph_vs_t as IGraphVsPtr foreign finalizer igraph_vs_destroy newtype #}
+{#pointer *igraph_vs_t as IGraphVs foreign finalizer igraph_vs_destroy newtype #}
 
-{#fun igraph_vs_all as ^ { + } -> `IGraphVsPtr' #}
+{#fun igraph_vs_all as ^ { + } -> `IGraphVs' #}
 
-{#fun igraph_vs_adj as ^ { +, `Int', `Neimode' } -> `IGraphVsPtr' #}
+{#fun igraph_vs_adj as ^ { +, `Int', `Neimode' } -> `IGraphVs' #}
 
-{#fun igraph_vs_vector as ^ { +, `VectorPtr' } -> `IGraphVsPtr' #}
+{#fun igraph_vs_vector as ^ { +, `Vector' } -> `IGraphVs' #}
 
 
 -- Vertex iterator
 
-{#pointer *igraph_vit_t as IGraphVitPtr foreign finalizer igraph_vit_destroy newtype #}
+{#pointer *igraph_vit_t as IGraphVit foreign finalizer igraph_vit_destroy newtype #}
 
 #c
-igraph_vit_t* igraph_vit_new(const igraph_t *graph, igraph_vs_t vs) {
-  igraph_vit_t* vit = (igraph_vit_t*) malloc (sizeof (igraph_vit_t));
-  igraph_vit_create(graph, vs, vit);
-  return vit;
-}
-
 igraph_bool_t igraph_vit_end(igraph_vit_t *vit) {
   return IGRAPH_VIT_END(*vit);
 }
@@ -45,15 +39,15 @@
 }
 #endc
 
-{#fun igraph_vit_new as ^ { `IGraphPtr', %`IGraphVsPtr' } -> `IGraphVitPtr' #}
+{#fun igraph_vit_create as igraphVitNew { `IGraph', %`IGraphVs', + } -> `IGraphVit' #}
 
-{#fun igraph_vit_end as ^ { `IGraphVitPtr' } -> `Bool' #}
+{#fun igraph_vit_end as ^ { `IGraphVit' } -> `Bool' #}
 
-{#fun igraph_vit_next as ^ { `IGraphVitPtr' } -> `()' #}
+{#fun igraph_vit_next as ^ { `IGraphVit' } -> `()' #}
 
-{#fun igraph_vit_get as ^ { `IGraphVitPtr' } -> `Int' #}
+{#fun igraph_vit_get as ^ { `IGraphVit' } -> `Int' #}
 
-vitToList :: IGraphVitPtr -> IO [Int]
+vitToList :: IGraphVit -> IO [Int]
 vitToList vit = do
     isEnd <- igraphVitEnd vit
     if isEnd
@@ -67,24 +61,18 @@
 
 -- Edge Selector
 
-{#pointer *igraph_es_t as IGraphEsPtr foreign finalizer igraph_es_destroy newtype #}
+{#pointer *igraph_es_t as IGraphEs foreign finalizer igraph_es_destroy newtype #}
 
-{#fun igraph_es_all as ^ { +, `EdgeOrderType' } -> `IGraphEsPtr' #}
+{#fun igraph_es_all as ^ { +, `EdgeOrderType' } -> `IGraphEs' #}
 
-{# fun igraph_es_vector as ^ { +, `VectorPtr' } -> `IGraphEsPtr' #}
+{# fun igraph_es_vector as ^ { +, `Vector' } -> `IGraphEs' #}
 
 
 -- Edge iterator
 
-{#pointer *igraph_eit_t as IGraphEitPtr foreign finalizer igraph_eit_destroy newtype #}
+{#pointer *igraph_eit_t as IGraphEit foreign finalizer igraph_eit_destroy newtype #}
 
 #c
-igraph_eit_t* igraph_eit_new(const igraph_t *graph, igraph_es_t es) {
-  igraph_eit_t* eit = (igraph_eit_t*) malloc (sizeof (igraph_eit_t));
-  igraph_eit_create(graph, es, eit);
-  return eit;
-}
-
 igraph_bool_t igraph_eit_end(igraph_eit_t *eit) {
   return IGRAPH_EIT_END(*eit);
 }
@@ -98,15 +86,15 @@
 }
 #endc
 
-{#fun igraph_eit_new as ^ { `IGraphPtr', %`IGraphEsPtr' } -> `IGraphEitPtr' #}
+{#fun igraph_eit_create as igraphEitNew { `IGraph', %`IGraphEs', + } -> `IGraphEit' #}
 
-{#fun igraph_eit_end as ^ { `IGraphEitPtr' } -> `Bool' #}
+{#fun igraph_eit_end as ^ { `IGraphEit' } -> `Bool' #}
 
-{#fun igraph_eit_next as ^ { `IGraphEitPtr' } -> `()' #}
+{#fun igraph_eit_next as ^ { `IGraphEit' } -> `()' #}
 
-{#fun igraph_eit_get as ^ { `IGraphEitPtr' } -> `Int' #}
+{#fun igraph_eit_get as ^ { `IGraphEit' } -> `Int' #}
 
-eitToList :: IGraphEitPtr -> IO [Int]
+eitToList :: IGraphEit -> IO [Int]
 eitToList eit = do
     isEnd <- igraphEitEnd eit
     if isEnd
@@ -119,9 +107,9 @@
 
 -- delete vertices
 
-{# fun igraph_delete_vertices as ^ { `IGraphPtr', %`IGraphVsPtr' } -> `Int' #}
+{# fun igraph_delete_vertices as ^ { `IGraph', %`IGraphVs' } -> `Int' #}
 
 
 -- delete edges
 
-{# fun igraph_delete_edges as ^ { `IGraphPtr', %`IGraphEsPtr' } -> `Int' #}
+{# fun igraph_delete_edges as ^ { `IGraph', %`IGraphEs' } -> `Int' #}
diff --git a/src/IGraph/Internal/Structure.chs b/src/IGraph/Internal/Structure.chs
--- a/src/IGraph/Internal/Structure.chs
+++ b/src/IGraph/Internal/Structure.chs
@@ -12,50 +12,50 @@
 
 #include "igraph/igraph.h"
 
-{#fun igraph_induced_subgraph as ^ { `IGraphPtr'
-                                   , id `Ptr (IGraphPtr)'
-                                   , %`IGraphVsPtr'
-                                   , `SubgraphImplementation' } -> `Int' #}
+{#fun igraph_induced_subgraph as ^ { `IGraph'
+                                   , +160
+                                   , %`IGraphVs'
+                                   , `SubgraphImplementation' } -> `IGraph' #}
 
-{#fun igraph_closeness as ^ { `IGraphPtr'
-                            , `VectorPtr'
-                            , %`IGraphVsPtr'
+{#fun igraph_closeness as ^ { `IGraph'
+                            , `Vector'
+                            , %`IGraphVs'
                             , `Neimode'
-                            , `VectorPtr'
+                            , `Vector'
                             , `Bool' } -> `Int' #}
 
-{#fun igraph_betweenness as ^ { `IGraphPtr'
-                              , `VectorPtr'
-                              , %`IGraphVsPtr'
+{#fun igraph_betweenness as ^ { `IGraph'
+                              , `Vector'
+                              , %`IGraphVs'
                               , `Bool'
-                              , `VectorPtr'
+                              , `Vector'
                               , `Bool' } -> `Int' #}
 
-{#fun igraph_eigenvector_centrality as ^ { `IGraphPtr'
-                                         , `VectorPtr'
+{#fun igraph_eigenvector_centrality as ^ { `IGraph'
+                                         , `Vector'
                                          , id `Ptr CDouble'
                                          , `Bool'
                                          , `Bool'
-                                         , `VectorPtr'
-                                         , `ArpackOptPtr' } -> `Int' #}
+                                         , `Vector'
+                                         , `ArpackOpt' } -> `Int' #}
 
-{#fun igraph_pagerank as ^ { `IGraphPtr'
+{#fun igraph_pagerank as ^ { `IGraph'
                            , `PagerankAlgo'
-                           , `VectorPtr'
+                           , `Vector'
                            , id `Ptr CDouble'
-                           , %`IGraphVsPtr'
+                           , %`IGraphVs'
                            , `Bool'
                            , `Double'
-                           , `VectorPtr'
+                           , `Vector'
                            , id `Ptr ()' } -> `Int' #}
 
-{#fun igraph_personalized_pagerank as ^ { `IGraphPtr'
+{#fun igraph_personalized_pagerank as ^ { `IGraph'
                                         , `PagerankAlgo'
-                                        , `VectorPtr'
+                                        , `Vector'
                                         , id `Ptr CDouble'
-                                        , %`IGraphVsPtr'
+                                        , %`IGraphVs'
                                         , `Bool'
                                         , `Double'
-                                        , `VectorPtr'
-                                        , `VectorPtr'
+                                        , `Vector'
+                                        , `Vector'
                                         , id `Ptr ()' } -> `Int' #}
diff --git a/src/IGraph/Isomorphism.hs b/src/IGraph/Isomorphism.hs
--- a/src/IGraph/Isomorphism.hs
+++ b/src/IGraph/Isomorphism.hs
@@ -25,7 +25,7 @@
     vpptr <- igraphVectorPtrNew 0
     igraphGetSubisomorphismsVf2 gptr1 gptr2 nullPtr nullPtr nullPtr nullPtr vpptr
         nullFunPtr nullFunPtr nullPtr
-    (map.map) truncate <$> vectorPPtrToList vpptr
+    (map.map) truncate <$> toLists vpptr
   where
     gptr1 = _graph g1
     gptr2 = _graph g2
diff --git a/src/IGraph/Layout.hs b/src/IGraph/Layout.hs
--- a/src/IGraph/Layout.hs
+++ b/src/IGraph/Layout.hs
@@ -41,7 +41,7 @@
 defaultKamadaKawai :: LayoutMethod
 defaultKamadaKawai = KamadaKawai
     { kk_seed = Nothing
-    , kk_nIter = 1000
+    , kk_nIter = 10
     , kk_sigma = \x -> fromIntegral x / 4
     , kk_startTemp = 10
     , kk_coolFact = 0.99
@@ -50,7 +50,7 @@
 
 defaultLGL :: LayoutMethod
 defaultLGL = LGL
-    { lgl_nIter = 150
+    { lgl_nIter = 100
     , lgl_maxdelta = \x -> fromIntegral x
     , lgl_area = area
     , lgl_coolexp = 1.5
@@ -68,18 +68,18 @@
                 Nothing -> igraphMatrixNew 0 0
                 Just xs -> if length xs /= nNodes gr
                                then error "Seed error: incorrect size"
-                               else listsToMatrixPtr $ (\(x,y) -> [x,y]) $ unzip xs
+                               else fromRowLists $ (\(x,y) -> [x,y]) $ unzip xs
 
             igraphLayoutKamadaKawai gptr mptr niter (sigma n) initemp coolexp
                 (kkconst n) (isJust seed) nullPtr nullPtr nullPtr nullPtr
-            [x, y] <- matrixPtrToColumnLists mptr
+            [x, y] <- toColumnLists mptr
             return $ zip x y
 
         LGL niter delta area coolexp repulserad cellsize -> do
             mptr <- igraphMatrixNew 0 0
             igraphLayoutLgl gptr mptr niter (delta n) (area n) coolexp
                 (repulserad n) (cellsize n) (-1)
-            [x, y] <- matrixPtrToColumnLists mptr
+            [x, y] <- toColumnLists mptr
             return $ zip x y
   where
     n = nNodes gr
diff --git a/src/IGraph/Motif.hs b/src/IGraph/Motif.hs
--- a/src/IGraph/Motif.hs
+++ b/src/IGraph/Motif.hs
@@ -55,6 +55,6 @@
 triadCensus gr = unsafePerformIO $ do
     vptr <- igraphVectorNew 0
     igraphTriadCensus (_graph gr) vptr
-    map truncate <$> vectorPtrToList vptr
+    map truncate <$> toList vptr
 
 -- motifsRandesu
diff --git a/src/IGraph/Mutable.hs b/src/IGraph/Mutable.hs
--- a/src/IGraph/Mutable.hs
+++ b/src/IGraph/Mutable.hs
@@ -1,132 +1,134 @@
 {-# LANGUAGE MultiParamTypeClasses #-}
-module IGraph.Mutable where
+module IGraph.Mutable
+    ( MGraph(..)
+    , MLGraph(..)
+    , setEdgeAttr
+    , setNodeAttr
+    , edgeAttr
+    , vertexAttr
+    , withVertexAttr
+    , withEdgeAttr
+    )where
 
-import           Control.Monad                  (when)
+import           Control.Monad                  (when, forM)
 import           Control.Monad.Primitive
 import qualified Data.ByteString.Char8          as B
+import           Data.Serialize                 (Serialize)
 import           Foreign
+import           Foreign.C.String               (CString, withCString)
 
 import           IGraph.Internal.Attribute
 import           IGraph.Internal.Data
 import           IGraph.Internal.Graph
 import           IGraph.Internal.Initialization
 import           IGraph.Internal.Selector
+import           IGraph.Types
 
--- constants
 vertexAttr :: String
 vertexAttr = "vertex_attribute"
 
 edgeAttr :: String
 edgeAttr = "edge_attribute"
 
-type LEdge a = (Int, Int, a)
+withVertexAttr :: (CString -> IO a) -> IO a
+withVertexAttr = withCString vertexAttr
+{-# INLINE withVertexAttr #-}
 
--- | Mutable labeled graph
-newtype MLGraph m d v e = MLGraph IGraphPtr
+withEdgeAttr :: (CString -> IO a) -> IO a
+withEdgeAttr = withCString edgeAttr
+{-# INLINE withEdgeAttr #-}
 
+-- | Mutable labeled graph.
+newtype MLGraph m d v e = MLGraph IGraph
+
 class MGraph d where
+    -- | Create a new graph.
     new :: PrimMonad m => Int -> m (MLGraph (PrimState m) d v e)
 
-    addNodes :: PrimMonad m => Int -> MLGraph(PrimState m) d v e -> m ()
+    -- | Add nodes to the graph.
+    addNodes :: PrimMonad m
+             => Int  -- ^ The number of new nodes.
+             -> MLGraph(PrimState m) d v e -> m ()
     addNodes n (MLGraph g) = unsafePrimToPrim $ igraphAddVertices g n nullPtr
 
-    addLNodes :: (Show v, PrimMonad m)
-                 => Int  -- ^ the number of new vertices add to the graph
-                 -> [v]  -- ^ vertices' labels
-                 -> MLGraph (PrimState m) d v e -> m ()
-    addLNodes n labels (MLGraph g)
-        | n /= length labels = error "addLVertices: incorrect number of labels"
-        | otherwise = unsafePrimToPrim $ do
-            let attr = makeAttributeRecord vertexAttr labels
-            alloca $ \ptr -> do
-                poke ptr attr
-                vptr <- listToVectorP [castPtr ptr]
-                withVectorPPtr vptr $ \p -> igraphAddVertices g n $ castPtr p
+    -- | Add nodes with labels to the graph.
+    addLNodes :: (Serialize v, PrimMonad m)
+              => [v]  -- ^ vertices' labels
+              -> MLGraph (PrimState m) d v e -> m ()
+    addLNodes labels (MLGraph g) = unsafePrimToPrim $ withVertexAttr $
+        \vattr -> asBSVector labels $ \bsvec -> with (mkStrRec vattr bsvec) $
+            \ptr -> do vptr <- fromPtrs [castPtr ptr]
+                       withVectorPtr vptr (igraphAddVertices g n . castPtr)
+      where
+        n = length labels
 
+    -- | Delete nodes from the graph.
     delNodes :: PrimMonad m => [Int] -> MLGraph (PrimState m) d v e -> m ()
     delNodes ns (MLGraph g) = unsafePrimToPrim $ do
-        vptr <- listToVector $ map fromIntegral ns
+        vptr <- fromList $ map fromIntegral ns
         vsptr <- igraphVsVector vptr
         igraphDeleteVertices g vsptr
         return ()
 
+    -- | Add edges to the graph.
     addEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m ()
-
-    addLEdges :: (PrimMonad m, Show e) => [LEdge e] -> MLGraph (PrimState m) d v e -> m ()
-
-    delEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m ()
-
-data U = U
-data D = D
-
-instance MGraph U where
-    new n = unsafePrimToPrim $ igraphInit >>= igraphNew n False >>= return . MLGraph
-
     addEdges es (MLGraph g) = unsafePrimToPrim $ do
-        vec <- listToVector xs
+        vec <- fromList xs
         igraphAddEdges g vec nullPtr
       where
         xs = concatMap ( \(a,b) -> [fromIntegral a, fromIntegral b] ) es
 
-    addLEdges es (MLGraph g) = unsafePrimToPrim $ do
-        vec <- listToVector $ concat xs
-        let attr = makeAttributeRecord edgeAttr vs
-        alloca $ \ptr -> do
-            poke ptr attr
-            vptr <- listToVectorP [castPtr ptr]
-            withVectorPPtr vptr $ \p -> igraphAddEdges g vec $ castPtr p
+    -- | Add edges with labels to the graph.
+    addLEdges :: (PrimMonad m, Serialize e) => [LEdge e] -> MLGraph (PrimState m) d v e -> m ()
+    addLEdges es (MLGraph g) = unsafePrimToPrim $ withEdgeAttr $ \eattr ->
+        asBSVector vs $ \bsvec -> with (mkStrRec eattr bsvec) $ \ptr -> do
+            vec <- fromList $ concat xs
+            vptr <- fromPtrs [castPtr ptr]
+            withVectorPtr vptr (igraphAddEdges g vec . castPtr)
       where
-        (xs, vs) = unzip $ map ( \(a,b,v) -> ([fromIntegral a, fromIntegral b], v) ) es
+        (xs, vs) = unzip $ map ( \((a,b),v) -> ([fromIntegral a, fromIntegral b], v) ) es
 
+    -- | Delete edges from the graph.
+    delEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m ()
+
+instance MGraph U where
+    new n = unsafePrimToPrim $ igraphInit >>= igraphNew n False >>= return . MLGraph
+
     delEdges es (MLGraph g) = unsafePrimToPrim $ do
-        vptr <- listToVector $ map fromIntegral eids
+        eids <- forM es $ \(fr, to) -> igraphGetEid g fr to False True
+        vptr <- fromList $ map fromIntegral eids
         esptr <- igraphEsVector vptr
         igraphDeleteEdges g esptr
         return ()
-      where
-        eids = flip map es $ \(fr, to) -> igraphGetEid g fr to False True
 
 instance MGraph D where
     new n = unsafePrimToPrim $ igraphInit >>= igraphNew n True >>= return . MLGraph
 
-    addEdges es (MLGraph g) = unsafePrimToPrim $ do
-        vec <- listToVector xs
-        igraphAddEdges g vec nullPtr
-      where
-        xs = concatMap ( \(a,b) -> [fromIntegral a, fromIntegral b] ) es
-
-    addLEdges es (MLGraph g) = unsafePrimToPrim $ do
-        vec <- listToVector $ concat xs
-        let attr = makeAttributeRecord edgeAttr vs
-        alloca $ \ptr -> do
-            poke ptr attr
-            vptr <- listToVectorP [castPtr ptr]
-            withVectorPPtr vptr $ \p -> igraphAddEdges g vec $ castPtr p
-      where
-        (xs, vs) = unzip $ map ( \(a,b,v) -> ([fromIntegral a, fromIntegral b], v) ) es
-
     delEdges es (MLGraph g) = unsafePrimToPrim $ do
-        vptr <- listToVector $ map fromIntegral eids
+        eids <- forM es $ \(fr, to) -> igraphGetEid g fr to True True
+        vptr <- fromList $ map fromIntegral eids
         esptr <- igraphEsVector vptr
         igraphDeleteEdges g esptr
         return ()
-      where
-        eids = flip map es $ \(fr, to) -> igraphGetEid g fr to True True
 
-setNodeAttr :: (PrimMonad m, Show v)
+-- | Set node attribute.
+setNodeAttr :: (PrimMonad m, Serialize v)
             => Int   -- ^ Node id
             -> v
             -> MLGraph (PrimState m) d v e
             -> m ()
-setNodeAttr nodeId x (MLGraph gr) = unsafePrimToPrim $ do
-    err <- igraphCattributeVASSet gr vertexAttr nodeId $ show x
-    when (err /= 0) $ error "Fail to set node attribute!"
+setNodeAttr nodeId x (MLGraph gr) = unsafePrimToPrim $ asBS x $ \bs ->
+    with bs $ \bsptr -> do
+        err <- igraphHaskellAttributeVASSet gr vertexAttr nodeId bsptr
+        when (err /= 0) $ error "Fail to set node attribute!"
 
-setEdgeAttr :: (PrimMonad m, Show v)
+-- | Set edge attribute.
+setEdgeAttr :: (PrimMonad m, Serialize e)
             => Int   -- ^ Edge id
-            -> v
+            -> e
             -> MLGraph (PrimState m) d v e
             -> m ()
-setEdgeAttr edgeId x (MLGraph gr) = unsafePrimToPrim $ do
-    err <- igraphCattributeEASSet gr edgeAttr edgeId $ show x
-    when (err /= 0) $ error "Fail to set edge attribute!"
+setEdgeAttr edgeId x (MLGraph gr) = unsafePrimToPrim $ asBS x $ \bs ->
+    with bs $ \bsptr -> do
+        err <- igraphHaskellAttributeEASSet gr edgeAttr edgeId bsptr
+        when (err /= 0) $ error "Fail to set edge attribute!"
diff --git a/src/IGraph/Structure.hs b/src/IGraph/Structure.hs
--- a/src/IGraph/Structure.hs
+++ b/src/IGraph/Structure.hs
@@ -7,36 +7,35 @@
     , personalizedPagerank
     ) where
 
-import Control.Monad
-import Foreign
-import Foreign.C.Types
-import System.IO.Unsafe (unsafePerformIO)
-import qualified Data.HashMap.Strict as M
-import Data.Hashable (Hashable)
+import           Control.Monad
+import           Data.Hashable             (Hashable)
+import qualified Data.HashMap.Strict       as M
+import           Data.Serialize            (Serialize)
+import           Foreign
+import           Foreign.C.Types
+import           System.IO.Unsafe          (unsafePerformIO)
 
-import IGraph
-import IGraph.Mutable
-import IGraph.Internal.Graph
-import IGraph.Internal.Data
-import IGraph.Internal.Selector
-import IGraph.Internal.Structure
-import IGraph.Internal.Arpack
-import IGraph.Internal.Constants
-import IGraph.Internal.Attribute
+import           IGraph
+import           IGraph.Internal.Arpack
+import           IGraph.Internal.Attribute
+import           IGraph.Internal.Constants
+import           IGraph.Internal.Data
+import           IGraph.Internal.Graph
+import           IGraph.Internal.Selector
+import           IGraph.Internal.Structure
+import           IGraph.Mutable
 
-inducedSubgraph :: (Hashable v, Eq v, Read v) => LGraph d v e -> [Int] -> LGraph d v e
+inducedSubgraph :: (Hashable v, Eq v, Serialize v) => LGraph d v e -> [Int] -> LGraph d v e
 inducedSubgraph gr vs = unsafePerformIO $ do
-    vs' <- listToVector $ map fromIntegral vs
+    vs' <- fromList $ map fromIntegral vs
     vsptr <- igraphVsVector vs'
-    mallocForeignPtrBytes 160 >>= \gptr -> withForeignPtr gptr $ \p -> do
-        igraphInducedSubgraph (_graph gr) p vsptr IgraphSubgraphCreateFromScratch
-        let g' = IGraphPtr gptr
-            labToId = M.fromListWith (++) $ zip labels $ map return [0..nV-1]
-            nV = igraphVcount g'
-            labels = map (read . igraphCattributeVAS g' vertexAttr) [0 .. nV-1]
-        return $ LGraph g' labToId
+    g' <- igraphInducedSubgraph (_graph gr) vsptr IgraphSubgraphCreateFromScratch
+    nV <- igraphVcount g'
+    labels <- forM [0 .. nV - 1] $ \i ->
+        igraphHaskellAttributeVAS g' vertexAttr i >>= fromBS
+    return $ LGraph g' $ M.fromListWith (++) $ zip labels $ map return [0..nV-1]
 
--- | closeness centrality
+-- | Closeness centrality
 closeness :: [Int]  -- ^ vertices
           -> LGraph d v e
           -> Maybe [Double]  -- ^ optional edge weights
@@ -44,42 +43,42 @@
           -> Bool   -- ^ whether to normalize
           -> [Double]
 closeness vs gr ws mode normal = unsafePerformIO $ do
-    vs' <- listToVector $ map fromIntegral vs
+    vs' <- fromList $ map fromIntegral vs
     vsptr <- igraphVsVector vs'
     vptr <- igraphVectorNew 0
     ws' <- case ws of
-        Just w -> listToVector w
-        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
+        Just w -> fromList w
+        _      -> liftM Vector $ newForeignPtr_ $ castPtr nullPtr
     igraphCloseness (_graph gr) vptr vsptr mode ws' normal
-    vectorPtrToList vptr
+    toList vptr
 
--- | betweenness centrality
+-- | Betweenness centrality
 betweenness :: [Int]
             -> LGraph d v e
             -> Maybe [Double]
             -> [Double]
 betweenness vs gr ws = unsafePerformIO $ do
-    vs' <- listToVector $ map fromIntegral vs
+    vs' <- fromList $ map fromIntegral vs
     vsptr <- igraphVsVector vs'
     vptr <- igraphVectorNew 0
     ws' <- case ws of
-        Just w -> listToVector w
-        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
+        Just w -> fromList w
+        _      -> liftM Vector $ newForeignPtr_ $ castPtr nullPtr
     igraphBetweenness (_graph gr) vptr vsptr True ws' False
-    vectorPtrToList vptr
+    toList vptr
 
--- | eigenvector centrality
+-- | Eigenvector centrality
 eigenvectorCentrality :: LGraph d v e
                       -> Maybe [Double]
                       -> [Double]
 eigenvectorCentrality gr ws = unsafePerformIO $ do
     vptr <- igraphVectorNew 0
     ws' <- case ws of
-        Just w -> listToVector w
-        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
+        Just w -> fromList w
+        _      -> liftM Vector $ newForeignPtr_ $ castPtr nullPtr
     arparck <- igraphArpackNew
     igraphEigenvectorCentrality (_graph gr) vptr nullPtr True True ws' arparck
-    vectorPtrToList vptr
+    toList vptr
 
 -- | Google's PageRank
 pagerank :: Graph d
@@ -87,29 +86,45 @@
          -> Maybe [Double]  -- ^ edge weights
          -> Double  -- ^ damping factor, usually around 0.85
          -> [Double]
-pagerank gr ws d = unsafePerformIO $ alloca $ \p -> do
-    vptr <- igraphVectorNew 0
-    vsptr <- igraphVsAll
-    ws' <- case ws of
-        Just w -> listToVector w
-        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
-    igraphPagerank (_graph gr) IgraphPagerankAlgoPrpack vptr p vsptr
-        (isDirected gr) d ws' nullPtr
-    vectorPtrToList vptr
+pagerank gr ws d
+    | n == 0 = []
+    | otherwise = unsafePerformIO $ alloca $ \p -> do
+        vptr <- igraphVectorNew 0
+        vsptr <- igraphVsAll
+        ws' <- case ws of
+            Just w -> if length w /= m
+                then error "pagerank: incorrect length of edge weight vector"
+                else fromList w
+            _ -> liftM Vector $ newForeignPtr_ $ castPtr nullPtr
+        igraphPagerank (_graph gr) IgraphPagerankAlgoPrpack vptr p vsptr
+            (isDirected gr) d ws' nullPtr
+        toList vptr
+  where
+    n = nNodes gr
+    m = nEdges gr
 
+-- | Personalized PageRank.
 personalizedPagerank :: Graph d
                      => LGraph d v e
                      -> [Double]   -- ^ reset probability
                      -> Maybe [Double]
                      -> Double
                      -> [Double]
-personalizedPagerank gr reset ws d = unsafePerformIO $ alloca $ \p -> do
-    vptr <- igraphVectorNew 0
-    vsptr <- igraphVsAll
-    ws' <- case ws of
-        Just w -> listToVector w
-        _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr
-    reset' <- listToVector reset
-    igraphPersonalizedPagerank (_graph gr) IgraphPagerankAlgoPrpack vptr p vsptr
-        (isDirected gr) d reset' ws' nullPtr
-    vectorPtrToList vptr
+personalizedPagerank gr reset ws d
+    | n == 0 = []
+    | length reset /= n = error "personalizedPagerank: incorrect length of reset vector"
+    | otherwise = unsafePerformIO $ alloca $ \p -> do
+        vptr <- igraphVectorNew 0
+        vsptr <- igraphVsAll
+        ws' <- case ws of
+            Just w -> if length w /= m
+                then error "pagerank: incorrect length of edge weight vector"
+                else fromList w
+            _ -> liftM Vector $ newForeignPtr_ $ castPtr nullPtr
+        reset' <- fromList reset
+        igraphPersonalizedPagerank (_graph gr) IgraphPagerankAlgoPrpack vptr p vsptr
+            (isDirected gr) d reset' ws' nullPtr
+        toList vptr
+  where
+    n = nNodes gr
+    m = nEdges gr
diff --git a/src/IGraph/Types.hs b/src/IGraph/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/IGraph/Types.hs
@@ -0,0 +1,16 @@
+
+module IGraph.Types where
+
+import qualified Data.HashMap.Strict   as M
+
+import           IGraph.Internal.Graph
+
+type Node = Int
+type Edge = (Node, Node)
+type LEdge a = (Edge, a)
+
+-- | Undirected graph.
+data U
+
+-- | Directed graph.
+data D
diff --git a/tests/Test/Attributes.hs b/tests/Test/Attributes.hs
new file mode 100644
--- /dev/null
+++ b/tests/Test/Attributes.hs
@@ -0,0 +1,60 @@
+module Test.Attributes
+    ( tests
+    ) where
+
+import           Conduit
+import           Control.Monad
+import           Control.Monad.ST
+import           Data.List
+import           Data.List.Ordered         (nubSort)
+import           Data.Maybe
+import           Data.Serialize
+import           Foreign
+import           System.IO.Unsafe
+import           Test.Tasty
+import           Test.Tasty.HUnit
+import           Test.Utils
+
+import           IGraph
+import           IGraph.Exporter.GEXF
+import           IGraph.Internal.Attribute
+import           IGraph.Mutable
+import           IGraph.Structure
+
+tests :: TestTree
+tests = testGroup "Attribute tests"
+    [ nodeLabelTest
+    , labelTest
+    , serializeTest
+    ]
+
+nodeLabelTest :: TestTree
+nodeLabelTest = testCase "node label test" $ do
+    let ns = sort $ map show [38..7000]
+        gr = mkGraph ns [] :: LGraph D String ()
+    assertBool "" $ sort (map (nodeLab gr) $ nodes gr) == ns
+
+labelTest :: TestTree
+labelTest = testCase "edge label test" $ do
+    dat <- randEdges 1000 10000
+    let es = sort $ zipWith (\a b -> (a,b)) dat $ map show [1..]
+        gr = fromLabeledEdges es :: LGraph D Int String
+        es' = sort $ map (\(a,b) -> ((nodeLab gr a, nodeLab gr b), edgeLab gr (a,b))) $ edges gr
+    assertBool "" $ es == es'
+
+serializeTest :: TestTree
+serializeTest = testCase "serialize test" $ do
+    dat <- randEdges 1000 10000
+    let es = map ( \(a, b) -> (
+            ( defaultNodeAttributes{_nodeZindex=a}
+            , defaultNodeAttributes{_nodeZindex=b}), defaultEdgeAttributes) ) dat
+        gr = fromLabeledEdges es :: LGraph D NodeAttr EdgeAttr
+        gr' :: LGraph D NodeAttr EdgeAttr
+        gr' = case decode $ encode gr of
+            Left msg -> error msg
+            Right r  -> r
+        es' = map (\(a,b) -> ((nodeLab gr' a, nodeLab gr' b), edgeLab gr' (a,b))) $ edges gr'
+    gr'' <- runConduit $ (yield $ encode gr) .| decodeC :: IO (LGraph D NodeAttr EdgeAttr)
+    let es'' = map (\(a,b) -> ((nodeLab gr'' a, nodeLab gr'' b), edgeLab gr'' (a,b))) $ edges gr''
+    assertBool "" $ sort (map show es) == sort (map show es') &&
+        sort (map show es) == sort (map show es'')
diff --git a/tests/Test/Basic.hs b/tests/Test/Basic.hs
--- a/tests/Test/Basic.hs
+++ b/tests/Test/Basic.hs
@@ -10,6 +10,7 @@
 import           Test.Tasty
 import           Test.Tasty.HUnit
 import           Test.Utils
+import Conduit
 
 import           IGraph
 import           IGraph.Mutable
@@ -39,14 +40,17 @@
 graphCreationLabeled = testGroup "Graph creation -- with labels"
     [ testCase "" $ assertBool "" $ nNodes gr == n && nEdges gr == m
     , testCase "" $ edgeList @=? (sort $ map (\(fr,to) ->
-        (nodeLab gr fr, nodeLab gr to)) $ edges gr)
+        ((nodeLab gr fr, nodeLab gr to), edgeLab gr (fr, to))) $ edges gr)
+    , testCase "" $ edgeList @=? (sort $ map (\(fr,to) ->
+       ((nodeLab gr' fr, nodeLab gr' to), edgeLab gr' (fr, to))) $ edges gr')
     ]
   where
-    edgeList = sort $ map (\(a,b) -> (show a, show b)) $ unsafePerformIO $ randEdges 10000 1000
-    n = length $ nubSort $ concatMap (\(a,b) -> [a,b]) edgeList
+    edgeList = zip (sort $ map (\(a,b) -> (show a, show b)) $ unsafePerformIO $
+        randEdges 10000 1000) $ repeat 1
+    n = length $ nubSort $ concatMap (\((a,b),_) -> [a,b]) edgeList
     m = length edgeList
-    gr = fromLabeledEdges $ zip edgeList $ repeat () :: LGraph D String ()
-
+    gr = fromLabeledEdges edgeList :: LGraph D String Int
+    gr' = runST $ fromLabeledEdges' edgeList yieldMany :: LGraph D String Int
 
 graphEdit :: TestTree
 graphEdit = testGroup "Graph editing"
diff --git a/tests/test.hs b/tests/test.hs
--- a/tests/test.hs
+++ b/tests/test.hs
@@ -2,6 +2,7 @@
 import qualified Test.Isomorphism as Isomorphism
 import qualified Test.Motif       as Motif
 import qualified Test.Structure   as Structure
+import qualified Test.Attributes as Attributes
 import           Test.Tasty
 
 main :: IO ()
@@ -10,4 +11,5 @@
     , Structure.tests
     , Motif.tests
     , Isomorphism.tests
+    , Attributes.tests
     ]
