Generalising ($) like Control.Category generalises (.)

前端 未结 2 734
耶瑟儿~
耶瑟儿~ 2021-01-12 07:56

I had a thought to generalise ($) like Control.Category generalises (.), and I\'ve done so with the code at the end of this post (also

2条回答
  •  攒了一身酷
    2021-01-12 08:23

    $ applies morphisms to values. The concept of a value seems trivial, but actually, general categories need to have no such notion. Morphisms are values (arrow-values... whatever), but objects (types) needn't actually contain any elements.

    However, in many categories, there is a special object, the terminal object. In Hask, this is the () type. You'll notice that functions () -> a are basically equivalent to a values themselves. Categories in which this works are called well-pointed. So really, the fundamental thing you need for something like $ to make sense is

    class Category c => WellPointed c where
      type Terminal c :: *
      point :: a -> Terminal c `c` a
      unpoint :: Terminal c `c` a -> a
    

    Then you can define the application operator by

    ($) :: WellPointed c => c a b -> a -> b
    f $ p = unpoint $ f . point p
    

    The obvious instance for WellPointed is of course Hask itself:

    instance WellPointed (->) where
      type Terminal c = ()
    --point :: a -> () -> a
      point a () = a
    --unpoint :: (() -> a) -> a
      unpoint f = f ()
    

    The other well-known category, Kleisli, is not an instance of WellPointed as I wrote it (it allows point, but not unpoint). But there are plenty of categories which would make for a good WellPointed instance, if they could properly be implemented in Haskell at all. Basically, all the categories of mathematical functions with particular properties (LinK, Grp, {{•}, Top}...). The reason these aren't directly expressible as a Category is that they can't have any Haskell type as an object; newer category libraries like categories or constrained-categories do allow this. For instance, I have implemented this:

    instance (MetricScalar s) => WellPointed (Differentiable s) where
      unit = Tagged Origin
      globalElement x = Differentiable $ \Origin -> (x, zeroV, const zeroV)
      const x = Differentiable $ \_ -> (x, zeroV, const zeroV)
    

    As you see, the class interface is actually a bit different from what I wrote above. There isn't one universally accepted way of implementing such stuff in Haskell yet... in constrained-categories, the $ operator actually works more like what Cirdec described.

提交回复
热议问题