像Control.Category一样推广(。)的推广($)

时间:2015-06-29 14:57:07

标签: haskell

我曾考虑将($)概括为Control.Category一般性(.),并且我已使用此帖末尾的代码(also ideone )。

在此代码中,我创建了一个名为FunctionObject的类。此类具有带有以下签名的函数($)

($) :: f a b -> a -> b

当然,我将(->)作为此类的一个实例,以便$继续使用普通函数。

但是这允许你创建特殊的函数,例如,知道它们自己的逆函数,如下例所示。

我已经得出结论有三种可能性之一:

  1. 我是第一个想到它的人。
  2. 其他人已经做过了,我正在重新发明轮子。
  3. 这是一个坏主意。
  4. 选项1似乎不太可能,我在hayoo上的搜索没有显示选项2,所以我怀疑选项3最有可能,但是如果有人能够解释为什么会这样做会很好。

    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
    

2 个答案:

答案 0 :(得分:9)

$将态射应用于。价值的概念似乎微不足道,但实际上,一般类别不需要这样的概念。态射是值(箭头值......等等),但对象(类型)实际上不需要包含任何元素。

但是,在许多类别中,都有一个特殊对象terminal object。在 Hask 中,这是()类型。您会注意到函数() -> a基本上等同于a值本身。其中有效的类别称为well-pointed。实际上,$之类的基本要素是

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

然后您可以通过

定义应用程序运算符
($) :: WellPointed c => c a b -> a -> b
f $ p = unpoint $ f . point p

WellPointed的明显例子当然是 Hask 本身:

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

另一个众所周知的类别Kleisli不是我编写的WellPointed实例(它允许point,但不允许unpoint 1}})。但是如果它们可以在Haskell中正确实现,那么有很多类别可以构成一个好的WellPointed实例。基本上,具有特定属性的所有数学函数类别(LinKGrp{{•}, Top} ...)。这些不能直接表达为Category的原因是它们不能将任何Haskell类型作为对象;较新的类别库(例如categoriesconstrained-categories)允许这样做。例如,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)

如您所见,the class interface实际上与我上面写的有点不同。在Haskell中没有一种普遍接受的方式来实现这样的东西......在constrained-categories中,$运算符实际上更像Cirdec描述的那样。

答案 1 :(得分:5)

在Haskell中有两个用于此类事物的抽象,一个使用Arrow s和另一个Applicative s。两者都可以分解为比base中使用的更小的部分。

如果你进入Arrow方向和Inspection Property,那么你可以为那些能够将任意函数提升到箭头中的箭头单独设置一个类。

class ArrowArr a where
    arr :: (b -> c) -> a b c

这与ArrowArr箭头相反,箭头可以将任意箭头拖放到函数中。

class ArrowFun a where
    ($) :: a b c -> (b -> c)

如果您只是将arrArrow分开,则会留下break down the capabilities of Arrows into component pieces

class Category a => ArrowLike a where
    fst   :: a (b, d) b
    snd   :: a (d, b) b
    (&&&) :: a b c -> a b c' -> a b (c,c')

如果你向Applicative方向走,这是一个arrow like categories that can construct and deconstruct tuplesApplicative没有pure”(名称为Copointed)。

class Copointed p where Source
    copoint :: p a -> a

class Functor f => Apply f where
  (<.>) :: f (a -> b) -> f a -> f b

当你这样做时,你通常会删除函数的Category,而是有一个类型构造函数C a来表示根据一组规则构造的值(包括函数值)。