Last active
May 17, 2021 09:14
-
-
Save TotallyNotChase/764f63a3b0da969765550d1fcea3c4ef to your computer and use it in GitHub Desktop.
The Typeclass Design Pattern 2: Electric Functoraloo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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