Skip to content

Instantly share code, notes, and snippets.

@3noch
Last active December 20, 2021 21:41
Show Gist options
  • Select an option

  • Save 3noch/eef18dba108be7db0441 to your computer and use it in GitHub Desktop.

Select an option

Save 3noch/eef18dba108be7db0441 to your computer and use it in GitHub Desktop.
Functors in Python
import types
global fmap_vtable
fmap_vtable = {}
def fmap_for(type):
def register_fmap(func):
fmap_vtable[type] = func
return func
return register_fmap
def fmap(fn, data):
return fmap_vtable[type(data)](fn, data)
apply = lambda fn, data: fn(data)
fmap_for(types.NoneType)(lambda fn, data: None)
fmap_for(int)(apply)
fmap_for(str)(apply)
fmap_for(float)(apply)
fmap_for(bool)(apply)
fmap_for(list)(map)
fmap_for(dict)(lambda fn, data: { key: fn(value) for key, value in data.iteritems() })
@3noch

3noch commented Nov 11, 2015

Copy link
Copy Markdown
Author

This is a Python implementation of Functors from category theory (and a relatively common concept in FP languages). The term "Functor" can be easily confused with its colloquial usage in C++, and some other languages, where it has essentially come to mean "Callable."

However, in category theory, Functor refers to "a mapping between categories." To a programmer, this basically means that a Functor is simply a mapping from one type of data to another, with a one important restriction. In general, a "mapping from one type of data another" can basically be any sort of conversion. For example, json.dumps converts a dictionary to a JSON-formatting string. But json.dumps is not a Functor, because there is one important law that a Functor must obey: the shape of the data cannot change. The easiest example of a true Functor is the map function. It takes a list and returns a list whose elements are modified. If you map the str function onto a list of numbers, you are mapping from one data type to another, but the shape has not changed. The input and output are both lists.

@xtofl

xtofl commented Dec 18, 2021

Copy link
Copy Markdown

This is so programmer-friendly an approach that I am inclined to pour it into a small and terribly useful and extremely light weight library I would call fmap. Would that be allright with you? (I don't know how this is licensed)

I want it to follow the motto 'make simple things simple, and hard things possible` (https://www.quora.com/What-is-the-story-behind-Alan-Kay-s-adage-Simple-things-should-be-simple-complex-things-should-be-possible/answer/Alan-Kay-11?ch=10&share=7a78ffce&srid=88EK)

pip install fmap
>>> from fmap import fmap, register_functor
>>> numbers = dict(one=1, two=2, three=3)
>>> fmap(lambda x: x*x, numbers)
{'one': 1, 'two': 4, 'three': 9}
>>> # and work with typings, too
>>> from typing import Optional
>>> register_functor(Optional, lambda fn, data: fn(data) if data else None)
>>> fmap[Optional](lambda x: x*x, None)
None
>>> fmap[Optional](lambda x: x*x, 3)
9
>>> fmap[Optional](lambda x: x*x, dict(one=1, znox=None, three=2)
{'one': 1, 'znox'=None, 'three'=9)}
...

@xtofl

xtofl commented Dec 18, 2021

Copy link
Copy Markdown

@3noch

3noch commented Dec 20, 2021

Copy link
Copy Markdown
Author

Cool! Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment