Live data from Hacker News

Contravariant Functors Are Weird

sanj.ink

11–20 of 26 posts

Re: Contravariant Functors Are Weird

#11
post #5
post #4

Contravariant functors are actually really nice. The classic example from maths is the spectrum (Spec) of a ring R. As a functor, Spec(R) = the set of prime ideals of R. An example anyone can understand: all the multiples of prime numbers p Z, in the integers Z. Let R -> S is a ring homomorphism, then there is an induced map Spec(S) -> Spec(R). Spec establishes a connection between the category of rings and topologic…

While I know precisely what you are saying (have been learning scheme theory this summer), this is hardly an accessible example to pure math undergrads , let alone someone who's attempting to learn some functional programming, with no heavy experience with abstract algebra. First of all, why use Spec? Use ideals/varieties, it contains roughly the same data, while being way better to intuit. I'll put my money where my…

Yes that is not bad, a good example.

Re: Contravariant Functors Are Weird

#12
post #10
post #5

Earlier quoted context omitted.

While I know precisely what you are saying (have been learning scheme theory this summer), this is hardly an accessible example to pure math undergrads , let alone someone who's attempting to learn some functional programming, with no heavy experience with abstract algebra. First of all, why use Spec? Use ideals/varieties, it contains roughly the same data, while being way better to intuit. I'll put my money where my…

I have undergrad-level understanding of both abstract algebra and functional programming, and I have absolutely no idea how either your or the GP's example are connected to contravariant functors as understood through the lens of functional programming.

Maybe a good example that ties both together is some sort of filter on a list? If you add more constraints (more processing) the list gets smaller.

Suppose I have a function f : x -> y. x -> y is also a functor in y. If you have an x-> y but want x->z, you can use y->z and adjust your original function by post processing the output of f. However suppose you want w -> y but you still only have f. You need to post process with w->x. So f is covariant in y, contravariant in x. Things change in the opposite direction of the adaptor.

Perhaps serialisation is a useful example?

Perhaps contravariance becomes even more useful if you use it in some profunctor dimap where you have a producer and consumer and process, eg moving through a data structure and pre/post processing things.

https://www.youtube.com/watch?v=OJtGECfksds

Re: Contravariant Functors Are Weird

#13
I'm not a functional programmer, but I like reading about it to expand my horizon.

Some days I even think "hey functional programming looks fun, I should try it!"

Then I read articles like this and realize that ship has sailed...

I'll be sticking to my procedural code, now with a light sprinkling of functional-ish concepts.

Re: Contravariant Functors Are Weird

#14
post #12
post #10

Earlier quoted context omitted.

I have undergrad-level understanding of both abstract algebra and functional programming, and I have absolutely no idea how either your or the GP's example are connected to contravariant functors as understood through the lens of functional programming.

Maybe a good example that ties both together is some sort of filter on a list? If you add more constraints (more processing) the list gets smaller. Suppose I have a function f : x -> y. x -> y is also a functor in y. If you have an x-> y but want x->z, you can use y->z and adjust your original function by post processing the output of f. However suppose you want w -> y but you still only have f. You need to post proc…

> You need to post process with w->x

Did you mean pre-process?

Re: Contravariant Functors Are Weird

#15

One thing I've never understood is polarity. To my understanding, positive types are defined in terms of their introduction rules and negative types are defined in terms of their elimination rules. However, don't types both have introduction and elimination rules, making them positive or negative based on how you choose to define them? Also, how does polarity (emphasis on introduction versus elimination rules) relate…

The idea of polarity comes from the category theoretic notion of a universal property. Nice types have introduction and elimination rules but for negative types the introduction rule is "reversible" whereas for positive types the elimination rule is.

As an example, the function type `A -> B` is negative because the function introduction rule

G, x:A |- M : B ---------------- G |- lam x. M : A -> B

is a bijection: the inverse is

G |- N : A -> B ------------------- G , x:A |- N x : B

The beta and eta equations encode exactly the two properties of this being a bijection.

Positive types, like sums/alternatives/coproducts have their elimination rule as their reversible rule, i.e. "pattern matching". So the rule

G , x1 : A1 |- K1 : B G , x2 : A2 |- K2 : B --------------------- G , x : A1 + A1 |- case x of { in1 x1. K1 | n2 x2. K2 }

Has an inverse

G , x : A1 + A2 |- N : B --------------------------- G , x1 : A1 |- N[in1 x1/x] G , x2 : A2 |- N[in2 x2/x]

The reason people say the positive types are "defined in terms of their introduction rules" is that you say "here are all the ways to build a term of this type" (in1 and in2 for sums) and then the elimination rule is exactly "pattern match on all of those possibilities". There is a dual way to think of the negative types which is "here are all the ways to use a term of this type" and the introduction form is a "co-pattern match" where you say "inspect all of the ways I can be used and say what to do in each case".

If you know about category theory then the idea is that some types are defined by representing a functor C -> Set (positives) and others by representing a functor C^op -> Set (negatives).

Variance is I would say is an orthogonal concept, except that the only primitive contravariant type former in lambda calculus is function which is negative.

Re: Contravariant Functors Are Weird

#16
Any function defines a covariant function by postcomposition. Take for example the string-length function (len : string -> int). We can take any other function which outputs a string, say (f : X -> string) where X is any fixed type, and produce a new function (g : X -> int) by g(x) = len(f(x)). So the len function defines a functor from the set of functions with signature (X -> string) to the set of functions with signature (X -> int). For concreteness, one can imagine this functor as replacing functions which output strings by functions which output the lengths of those strings instead.

However we can also treat (len : string -> int) as a contravariant functor by pre-composing instead of post-composing. Say we have a function (h : int -> Y), then we can form (k : string -> Y) by setting k(s) = k(len(s)). This could be useful if we only cared about whether a strings length were a multiple of 5, say.

Some of the above are lies: in order to call these functors in the mathematical sense (or the Haskell sense) you need to phrase things in just the right way. But I think it gets the idea across about how simple the difference between co- and contra- variance can be, with the example of replacing f(x) by g(f(x)) or by f(g(x)).

Re: Contravariant Functors Are Weird

#17

I'm not a functional programmer, but I like reading about it to expand my horizon. Some days I even think "hey functional programming looks fun, I should try it!" Then I read articles like this and realize that ship has sailed... I'll be sticking to my procedural code, now with a light sprinkling of functional-ish concepts.

Honestly, concepts like functors are not that hard, they're way more intuitive than some of the OOP concepts when you get used to them a little bit.

Re: Contravariant Functors Are Weird

#18

I'm not a functional programmer, but I like reading about it to expand my horizon. Some days I even think "hey functional programming looks fun, I should try it!" Then I read articles like this and realize that ship has sailed... I'll be sticking to my procedural code, now with a light sprinkling of functional-ish concepts.

Honestly, concepts like functors are not that hard, they're way more intuitive than some of the OOP concepts when you get used to them a little bit.

Yeah I guess it's mostly about not knowing Haskell syntax so the code samples don't really do much for me, and not knowing any category theory so that's no use either.

I mean the Haskell wiki[1] is no use to a guy like me. Not a complaint, it's a reference after all. Wikipedia[2] isn't much better, which I do find slightly disappointing.

At least the Wikipedia article has some links to the concepts involved so should be doable to interpret the terse article after a bit of extra reading.

[1]: https://wiki.haskell.org/Functor

[2]: https://en.wikipedia.org/wiki/Functor_(functional_programmin...

Re: Contravariant Functors Are Weird

#19
Here's a description which is close to the way this arose in math. It's pretty simple. Maybe it will be helpful for some people to see it removed from programming issues. (Maybe not.)

From concreteness, let's suppose we're talking about sets and functions between sets. (The same thing works in an arbitrary category.) Thus, if X and Y are sets, Hom(X, Y) denotes the set of functions (morphisms) from X to Y. Suppose you have a fixed function f: A -> B. You can compose it with functions into A or out of B.

If g: C -> A maps into A, then f o g (using "o" to denote composition) gives a function from C to B: that is, f o g: C -> A -> B. We started with a function from C to A and wound up with a function from C to B. So we actually have a function (functor) Hom(C, A) -> Hom(C, B), which is often denoted Hom(C, f). We say that Hom(C, -) is covariant, because it respects the direction of arrows in the second slot. (f went from A to B, and Hom(C, f) goes from Hom(C, A) to Hom(C, B).)

If h: B -> D maps out of B, then h o f gives a function from A to D: that is, h o f: A -> B -> D. So we actually have a function from Hom(B, D) -> Hom(A, D) which is often denoted Hom(f, D). We say that Hom(-, D) is contravarient, because it reverses the direction of arrows in the first slot. (f went from A to B, but Hom(f, D) goes from Hom(B, D) to Hom(A, D).)

     g      f      h
  C ---> A ---> B ---> D
Thus, Hom(-, -) is actually a bifunctor which is covariant in one variable and contravariant in the other. Contravariant functors can be regarded as covariant functors on the opposite category. What is happening with Hom is a prototype for many of the ways that "covariance" and "contravariance" occur in math; for example, covariant and contravariant tensors. (The vector dual space function Hom(-, K) [where K is the ground field] is contravariant.)

One of the earliest descriptions of category theory (including variance) is in: Samuel Eilenberg and Saunders MacLane, "General Theory of Natural Equivalences". Transactions of the American Mathematical Society, Vol. 58, No. 2, (Sep., 1945), pp. 231-294

It's actually fairly readable. There have been many books and articles on category theory since then, and many specifically directed toward computer science (e.g. Michael Barr and Charles Wells, "Category Theory for Computer Science" - https://www.math.mcgill.ca/triples/Barr-Wells-ctcs.pdf).

Re: Contravariant Functors Are Weird

#20

Earlier quoted context omitted.

Honestly, concepts like functors are not that hard, they're way more intuitive than some of the OOP concepts when you get used to them a little bit.

Yeah I guess it's mostly about not knowing Haskell syntax so the code samples don't really do much for me, and not knowing any category theory so that's no use either. I mean the Haskell wiki[1] is no use to a guy like me. Not a complaint, it's a reference after all. Wikipedia[2] isn't much better, which I do find slightly disappointing. At least the Wikipedia article has some links to the concepts involved so should…

The problem is also that in order to generically define functors, one needs higher-kinded types. The Haskell/Scala code on the Wikipedia page is not translatable to say, Java.
Post reply on HN