Category Theory for Programmers
Kleisli Categories
Kleisi Categories
Composition and Identity on Embellished Types
func atoi(s string) (int64, error) {
i64, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return 0, err
}
return i64, nil
}
func unmarshal(data []byte) (string, error) {
var s string
err := json.Unmarshal(data, &s)
if err != nil {
return "", err
}
return s, nil
}
func atoi(s string) (int64, error) {
i64, err := strconv.ParseInt(s, 10, 64)
return i64, err
}
func unmarshal(data []byte) (s string, err error) {
err = json.Unmarshal(data, &s)
return
}
func atoi(s string) (int64, error) {
i64, err := strconv.ParseInt(s, 10, 64)
return i64, err
}
func toNum(data []byte) (int64, error) {
s, err := unmarshal(data)
if err != nil {
return err
}
return atoi(s)
}
func toNum(data []byte) (int64, error) {
s, err := unmarshal(data)
if err != nil {
return 0, err
}
i, err := atoi(s)
if err != nil {
return 0, err
}
return i, nil
}
func unmarshal(data []byte) (s string, err error) {
err = json.Unmarshal(data, &s)
return
}
func atoi(s string) (int64, error) {
i64, err := strconv.ParseInt(s, 10, 64)
return i64, err
}
// type ValueOrError = (A, error)
func toNum(data []byte) (int64, error) {
s, err := unmarshal(data)
if err != nil {
return 0, err
}
i, err := atoi(s)
if err != nil {
return 0, err
}
return i, nil
}
func unmarshal(data []byte) (s string, err error) {
err = json.Unmarshal(data, &s)
return
}
func atoi(s string) (int64, error) {
i64, err := strconv.ParseInt(s, 10, 64)
return i64, err
}
// type ValueOrError = (T, error)
type A = []byte
type B = string
type C = int64
type F = func(A) (B, error)
type G = func(B) (C, error)
type GdotF = func(A) (C, error)
func deriveCompose(f F, g G) GdotF {
return func(a A) (C, error) {
b, err := f(a)
if err != nil {
return 0, err
}
c, err := g(b)
if err != nil {
return 0, err
}
return c, nil
}
}
func toNum(data []byte) (int64, error) {
return deriveCompose(unmarshal, atoi)(data)
}
Error Category Composition
Error Category Identity
func Identity(value T) (T, error) {
return value, nil
}
Kleisli Error Handling
func upgradeUser(endpoint, username string) error {
getEndpoint := fmt.Sprintf("%s/oldusers/%s", endpoint, username)
postEndpoint := fmt.Sprintf("%s/newusers/%s", endpoint, username)
resp, err := http.Get(genEndpoint)
if err != nil {
return err
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
olduser, err := user.NewFromJson(data)
if err != nil {
return err
}
newuser, err := user.NewUserFromUser(olduser),
if err != nil {
return err
}
buf, err := json.Marshal(newuser)
if err != nil {
return err
}
_, err = http.Post(
postEndpoint,
"application/json",
bytes.NewBuffer(buf),
)
return err
}
Kleisli Error Handling
func upgradeUser(endpoint, username string) error {
getEndpoint := fmt.Sprintf("%s/oldusers/%s", endpoint, username)
postEndpoint := fmt.Sprintf("%s/newusers/%s", endpoint, username)
_, err := deriveCompose(
http.Get,
func(resp *http.Response) ([]byte, error) {
return ioutil.ReadAll(resp.Body)
},
newUserFromJson,
newUserFromUser,
json.Marshal,
func(buf []byte) (*http.Response, error) {
return http.Post(
postEndpoint,
"application/json",
bytes.NewBuffer(buf),
)
},
)(getEndpoint)
return err
}
Monads
(>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
(>=>) f g = \a -> f a >>= g
(>>=) :: m b -> (b -> m c) -> m c
(>>=) = join . fmap
fmap :: (a -> b) -> f a -> f b
fmap :: (b -> m c) -> m b -> m m c
join :: m m c -> m c
A function that is not defined for all possible values of its argument is called a partial function. It’s not really a function in the mathematical sense, so it doesn’t fit the standard categorical mold. It can, however, be represented by a function that returns an embellished type optional:
template<class A> class optional {
bool _isValid;
A _value;
public:
optional() : _isValid(false) {}
optional(A v) : _isValid(true), _value(v) {}
bool isValid() const { return _isValid; }
A value() const { return _value; }
};
optional<double> safe_root(double x) {
if (x >= 0) return optional<double>{sqrt(x)};
else return optional<double>{};
}
As an example, here’s the implementation of the embellished function safe_root:
- Construct the Kleisli category for partial functions
(define composition and identity).
template<class A, class B, class C>
function<optional<C>(A)> compose(
function<optional<B>(A)> f,
function<optional<C>(B)> g) {
return [f, g](A a) {
auto b = f(a);
if (!b.isValid()) {
return <optional><C>{};
}
auto c = g(b.value());
return c;
};
}
template<class A> optional<A> identity(A) {
return optional<A>{x};
}
2. Implement the embellished function `safe_reciprocal` that returns a valid reciprocal of its argument, if it’s different from zero.
optional<double> safe_reciprocal(double x) {
if (x != 0) return optional<double>{1 / x};
else return optional<double>{};
}
3. Compose `safe_root` and `safe_reciprocal` to implement `safe_root_reciprocal` that calculates sqrt(1/x) whenever possible.
optional<double> safe_root_reciprocal(double x) {
return compose(safe_reciprocal, safe_root)(x);
}
category-theory-for-programmers-104
By Walter Schulze
category-theory-for-programmers-104
- 1,063