Search code examples
haskellfunctional-programmingfunctor

What's the meaning of `fmap` over set of predicates in Haskell?


My student assignment I'm doing for Haskell programming includes a task I'm a little bit puzzled to solve. The things are given so: an instance of Functor class to be created just for a set-based new type. There is a declaration of that:

newtype Set x = Set { contains :: (x -> Bool) }

It's a case for me to understand what means if fmap serves to be applied to something like a set of predicates. When doing about previous tasks, I've already defined fmap rather with functions like (+3) (to alter integers), (toUpper) (to strings) etc. It's first time I'm dealing with anything beyond normal types (various numerics, String, Char). There is my humble attempt to start:

instance Functor Set where
    fmap f (Set x) = if (contains x) == True then Set (f x) else Set x

Surely, it's a nonsense code, but I suppose some True/False need to be evaluated before fmap apllication goes well. But, first of all, would you please explain the matter of set of predicates to elaborate more sensible approach?


Solution

  • With this definition, it is actually impossible to define a Functor instance for Set. The reason for this is that your Set a type contains a in a negative position... that is, a is an argument to a function, not a value. When that happens, the type constructor can be a contravariant functor (type class Contravariant from the contravariant package), but it cannot be a covariant functor (the Functor type class).

    Here's are definitions of those classes:

    class Functor f where
        fmap :: (a -> b) -> f a -> f b
    
    class Contravariant f where
        contramap :: (a -> b) -> f b -> f a
    

    See the difference? In a contravariant functor, the direction of the function you pass in is flipped when it's lifted to operate on the functor type.

    In the end, this should make sense. Your notion of a "set" is a test that tells you whether something qualifies or not. This is a perfectly good mathematical definition of a set, but it gives you different computational powers than the standard one. For example, you cannot get at the elements of your predicate-based sets; only wait to be given a potential element. If you have a Set Integer, and a function f :: String -> Integer, then you can test Strings by first converting them to Integers and testing those, so you can get from Set Integer to Set String. But having a g :: Integer -> String doesn't let you test Strings!

    If this is an assignment, then either you've misunderstood the assignment, you've done some earlier step differently than expected (e.g., if you defined Set yourself in an earlier part, maybe you need to change the definition), or perhaps your instructor is hoping you'll struggle with this and understand why Functor cannot be defined.