Search code examples
javahaskellcategory-theory

Functors in Java


I'm trying to define classes in Java that are similar to Haskell's functors. Hereby, a functor is defined as:

/**
 * Programming languages allow only (just simply enough) endofunctor, that are functors from and to the same category.
 * In this case, the category is the one of the datatypes (in here Type, in order to make it more clear)
 */
public interface EndoFunctor<X extends Type> extends Type {

    /**
     * The basic implementation for any element fx
     * @param map   Transformation function for the type parameter
     * @param fx    Element of the current class
     * @param <Y>   Target type
     * @return      transformed element through map
     */
    <Y extends Type> EndoFunctor<Y> fmap(Function<X,Y> map, EndoFunctor<X> fx);

}

If I want to implement a Identity Functor over types functor, I have to write something like

public class Id<X extends Type> implements EndoFunctor<X> {
    protected X witness;
    Id(X witness) { this.witness = witness; }
    @Override
    public <Y extends Type> Id<Y> fmap(Function<X, Y> map, Id<X> fx) {
        return new Id<>(map.apply(fx.witness));
    }
}

The problem with this code is that Id<X> does not match the type EndoFunctor<X>. How could I determine fmap in the EndoFunctor interface such that if any type K<T> implements EndoFunctor<T> and a map function T->U is given, then K<U> is returned as a value, without any typecasting (that is, since I know that my object is an Id<T>, then the result of fmap "has to be" a Id<U>, and hence I downcast the result of type EndoFunctor<U> to such type)?


Solution

  • You could use CRTP:

    interface EndoFunctor<X extends Type, T extends EndoFunctor<X, T>> extends Type {
        <Y extends Type> EndoFunctor<Y, ?> fmap(Function<X,Y> map, T fx);    
    }
    
    class Id<X extends Type> implements EndoFunctor<X, Id<X>> {
        protected X witness;
        Id(X witness) { this.witness = witness; }
    
        @Override
        public <Y extends Type> Id<Y> fmap(Function<X, Y> map, Id<X> fx) {
            return new Id<>(map.apply(fx.witness));
        }
    }