Skip to content

Instantly share code, notes, and snippets.

@TotallyNotChase
Last active May 17, 2021 09:14
Show Gist options
  • Save TotallyNotChase/764f63a3b0da969765550d1fcea3c4ef to your computer and use it in GitHub Desktop.
Save TotallyNotChase/764f63a3b0da969765550d1fcea3c4ef to your computer and use it in GitHub Desktop.
The Typeclass Design Pattern 2: Electric Functoraloo
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Functor(T, A, B) Functor##T##A##B
#define Functorable(T, A, B) Functorable##T##A##B
#define DefineFunctorOf(ReturnT, A, B) \
typedef struct \
{ \
ReturnT (*const fmap)(void* self, B (*const)(A a)); \
} Functor(ReturnT, A, B); \
typedef struct functorable##ReturnT##A##B \
{ \
void* self; \
Functor(ReturnT, A, B) const* tc; \
} Functorable(ReturnT, A, B)
#define impl_functor(T, ReturnT, A, B, Name, fmap_f) \
Functorable(ReturnT, A, B) Name(T x) \
{ \
ReturnT (*const fmap_)(T self, B(*const)(A a)) = (fmap_f); \
(void)fmap_; \
static Functor(ReturnT, A, B) const tc = {.fmap = (ReturnT (*const)(void*, B (*const)(A)))(fmap_f)}; \
return (Functorable(ReturnT, A, B)){.tc = &tc, .self = x}; \
}
typedef struct
{
size_t size;
int* arr;
} IntArr, *IntArrP;
/* Functor returning concrete type */
DefineFunctorOf(IntArrP, int, int);
static IntArrP intarr_fmap(IntArr* iarr, int (* const mapf)(int a))
{
int* const resarr = malloc(iarr->size * sizeof(*resarr));
for (size_t i = 0; i < iarr->size; i++)
{
resarr[i] = mapf(iarr->arr[i]);
}
IntArr* iresarr = malloc(sizeof(*iarr));
iresarr->arr = resarr;
iresarr->size = iarr->size;
return iresarr;
}
static impl_functor(IntArr*, IntArrP, int, int, prep_intarr_functor, intarr_fmap)
static int incr(int x) { return x + 1; }
/*
Capabilities: Bring in iterables from c-iterators + a few more typeclasses to have
functors returning generic typeclass constraints, with any level of typeclass nesting
e.g Functor of Iterable(int), that works on functions of type `int -> Showable`, whose fmap returns an iterable of
`Showable`s
fmap signature- `(int -> Showable) -> Iterable(int) -> Iterable(Showable)`
Problem: ReturnT cannot have constraints, someone could make a `Functor(Showable, int, string)` and implement it for `Iterable(int)`
That's a functor taking an iterable but whose fmap returns a `Showable` - breaking the functor law(s)
*/
int main(void)
{
int arr[] = { 42, 3, 1, 94 };
IntArr ia = { .size = sizeof(arr) / sizeof(*arr), .arr = arr };
Functorable(IntArrP, int, int) fnctr = prep_intarr_functor(&ia);
IntArr* mapped = fnctr.tc->fmap(fnctr.self, incr);
for (size_t i = 0; i < mapped->size; i++)
{
printf("%d ", mapped->arr[i]);
}
puts("");
free(mapped->arr);
free(mapped);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment