Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing Haskell's Functor for Metaprogramming

My knowledge of category theory isn’t very good. So please bear with me.

I’ve been reading Monads Made Difficult

and saw the following definition.

class (Category c, Category d) => Functor c d t where
  fmap :: c a b -> d (t a) (t b)

from that site I read that the Functor type in Haskell’s prelude is really an Endofunctor. (where both category c and d in the above class are Hask)

After reading through the page, I was wondering. Would Haskell be better suited for meta-programming if it used real Functors and not just Endofunctors?

say we have the following (the Js stands for Javascript)

data Js a where
  litInt :: Integer -> Js Integer
  litString :: String -> Js String
  var :: String -> Js a
  lam :: String -> Js b -> Js (a -> b)
  let :: String -> Js a -> Js b -> Js b
  ap :: JsFunc a b -> Js a -> Js b

type JsFunc a b = Js (a -> b)

now back to the definition of Functor above

class (Category c, Category d) => Functor c d t where
  fmap :: c a b -> d (t a) (t b)

we can have c be JsFunc, d be Hask and have t be Js

this will give us a Functor for Js. That I could not do using Haskell’s Endofunctor.

I could not find anything for the Apply or Applicative type-classes defined in the same way as this Functor. Could the same be done with those too?

And correct me if I’m wrong. But the same can not be done for Monad, because it really an instance of an Endofunctor?

like image 799
clinux Avatar asked Apr 12 '15 09:04

clinux


People also ask

Is functor a Typeclass?

Functor in Haskell is a typeclass that provides two methods – fmap and (<$) – for structure-preserving transformations. To implement a Functor instance for a data type, you need to provide a type-specific implementation of fmap – the function we already covered.

Is a monad a functor?

A functor is an interface with one method i.e a mapping of the category to category. Monads basically is a mechanism for sequencing computations. A monad is a way to wrap stuff, then operate on the wrapped stuff without unwrapping it.

Is functor a type constructor?

We use the word “functor” to refer to type constructors that meet certain criteria. Most often, what we mean is a type constructor that has a sensible instance of the Functor class. But fmap isn't the only mapping between types that exists.

What are Functors Applicatives and monads?

A functor is a data type that implements the Functor typeclass. An applicative is a data type that implements the Applicative typeclass. A monad is a data type that implements the Monad typeclass. A Maybe implements all three, so it is a functor, an applicative, and a monad.


1 Answers

Well, first –

... if it used real Functors and not just Endofunctors ...

Haskell functors are real functors. Only, the Functor class doesn't allow any kind of general functors, but only the specific ones that are endofunctors on Hask.

Indeed, non-endo–functors are interesting; mathematicians use them all the time. And while as you say, there can't be non-endofunctor monads, an analogue to Applicative is possible: that class is really just a nice Haskell interface for monoidal functors, which can be defined between different categories. Looks about like this:

class (Functor r t f, Category r, Category t) => Monoidal r t f where
  pureUnit :: () `t` f ()
  fzipWith :: (a, b) `r` c -> (f a, f b) `t` f c

To actually use this anywhere as conveniently as the standard Applicative class though, you'll need a bit more than just the Monoidal class. This has been tryied in a couple of libraries. I also wrote one. But, well... it doesn't exactly look beautiful...

class (Functor f r t, Cartesian r, Cartesian t) => Monoidal f r t where
  pureUnit :: UnitObject t `t` f (UnitObject r)
  fzipWith :: (ObjectPair r a b, Object r c, ObjectPair t (f a) (f b), Object t (f c))
              => r (a, b) c -> t (f a, f b) (f c)

class (Monoidal f r t, Curry r, Curry t) => Applicative f r t where
  -- ^ Note that this tends to make little sense for non-endofunctors. 
  --   Consider using 'constPure' instead.
  pure :: (Object r a, Object t (f a)) => a `t` f a 

  (<*>) :: ( ObjectMorphism r a b
           , ObjectMorphism t (f a) (f b), Object t (t (f a) (f b))
           , ObjectPair r (r a b) a, ObjectPair t (f (r a b)) (f a)
           , Object r a, Object r b )
       => f (r a b) `t` t (f a) (f b)
  (<*>) = curry (fzipWith $ uncurry id)

I don't know if any of these frameworks would allow you to write the applicative JS code you want in a practical way. Possibly, but actually I rather suspect it would get very messy. Also note that, while you now have applicatives floating around, this doesn't mean you can actually do what's normally done with Hask applicatives in Js code – on the contrary, you'd actually need to wrap those applicatives as transformers around the Js type.

You might want to consider abandoning “JS values” as such completely, and only write in a category/arrow paradigm instead. I.e., turn the definition around:

data JsFunc a b where
    litInt :: Integer -> JsFunc () Integer
    litString :: String -> JsFunc () String
    var :: String -> JsFunc () a
    lam :: String -> JsFunc () b -> JsFunc a b
    let :: String -> JsFunc () a -> JsFunc () b -> JsFunc () b
    ap :: JsFunc a b -> JsFunc () a -> JsFunc () b

type Js = JsFunc ()

here, we use () as the terminal object of the JsFunc category – which makes the Js a ≡ JsFunc () a arrows equivalent to what we normally call values (at least if we don't consider strictness semantics). If you go that route, pretty much everything needs to be written point-free, but there's syntax to pretend otherwise, and you can nicely incorporate functors and even monads (as Kleisli arrows).

like image 99
leftaroundabout Avatar answered Nov 05 '22 12:11

leftaroundabout