泛化 ($) 如 Control.Category 泛化 (.)

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

我想像 Control.Category 概括 (.) 那样概括 ($),并且我已经使用 post 末尾的代码实现了这一点(also ideone).

在这段代码中,我创建了一个名为 FunctionObject 的 class。这个 class 有一个函数 ($),签名如下:

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

自然地,我将 (->) 作为此 class 的一个实例,因此 $ 可以继续使用普通函数。

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

我得出的结论是三种可能性之一:

  1. 我是第一个想到的
  2. 其他人已经做到了,我正在重新发明轮子。
  3. 这是个坏主意。

选项 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

在Haskell中有两种抽象用于类似的事情,一种使用Arrows,另一种使用Applicatives。两者都可以分解成比 base.

中使用的更小的部分

如果您朝 Arrow 方向和 break down the capabilities of Arrows into component pieces 方向前进,您将有一个单独的 class 用于那些能够将任意函数提升到箭头中的箭头。

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

这与 ArrowArr 箭头相反,其中任意箭头都可以放置到函数中。

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

如果您只是将 arrArrow 中拆分出来,您将剩下 arrow like categories that can construct and deconstruct tuples

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 方向走,这是一个 Copointed "Applicative without pure" (which goes by the name Apply)。

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 表示根据一组特定规则构造的值(包括函数值)。

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

然而,在许多类别中,有一个特殊的对象,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。所以说真的,要让 $ 这样的东西有意义,你需要的基本东西是

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)。但是有很多类别可以构成一个好的 WellPointed 实例,如果它们完全可以在 Haskell 中正确实现的话。基本上,所有具有特定属性的数学函数类别 (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)

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