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 ideone).
In this code I've created a class called FunctionObject
. This class has a function ($)
with the following signature:
($) :: f a b -> a -> b
Naturally I make (->)
an instance of this class so $
continues to work with ordinary functions.
But this allows you to make special functions that, for example, know their own inverse, as the example below shows.
I've concluded there's one of three possibilities:
Option 1 seems unlikely, and my searches on hayoo didn't reveal option 2, so I suspect option 3 is most likely, but if someone could explain why that is it would be good.
import Prelude hiding ((.), ($))
import Control.Category ((.), Category)
class FunctionObject f where
($) :: f a b -> a -> b
infixr 0 $
instance FunctionObject (->) where
f $ x = f x
data InvertibleFunction a b =
InvertibleFunction (a -> b) (b -> a)
instance Category InvertibleFunction where
(InvertibleFunction f f') . (InvertibleFunction g g') =
InvertibleFunction (f . g) (g' . f')
instance FunctionObject InvertibleFunction where
(InvertibleFunction f _) $ x = f $ x
inverse (InvertibleFunction f f') = InvertibleFunction f' f
add :: (Num n) => n -> InvertibleFunction n n
add n = InvertibleFunction (+n) (subtract n)
main = do
print $ add 2 $ 5 -- 7
print $ inverse (add 2) $ 5 -- 3
$
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With