8

我想像泛化($)一样进行Control.Category泛化(.),并且我已经使用本文末尾的代码(也是 ideone)这样做了。

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

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

自然地,我创建(->)了这个类的一个实例,以便$继续使用普通函数。

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

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

  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
4

2 回答 2

12

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

但是,在许多类别中,都有一个特殊的对象,即终端对象。在Hask中,这是()类型。您会注意到函数() -> a基本上等同于a值本身。这工作的类别被称为有针对性的。所以真的,你需要的基本东西是$有意义的

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 中正确实现的话。基本上,所有具有特定属性的数学函数类别(Lin KGrp{{•}、Top } ...)。这些不能直接表示为 a 的原因Category是它们不能将任何 Haskell 类型作为对象。较新的类别库(如类别受限类别)确实允许这样做。例如,我已经实现了这个

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)

如您所见,类接口实际上与我上面写的有点不同。在 Haskell 中还没有一种普遍接受的方式来实现这些东西……在 中constrained-categories$操作符实际上更像 Cirdec 所描述的那样工作。

于 2015-06-29T16:06:40.630 回答
6

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


如果您朝着这个Arrow方向前进并将s的功能分解Arrow为组件,您将拥有一个单独的类,用于那些能够将任意功能提升到箭头中的箭头。

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

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

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

如果你只是分开arrArrow你会留下像可以构造和解构元组的类别的箭头

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朝这个方向走,这是一个CopointedApplicative没有pure”(它的名字Apply)。

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

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

当您采用这种方式时,您通常会删除Categoryfor 函数,而是使用一个类型构造函数来C a表示根据一组规则构造的值(包括函数值)。

于 2015-06-29T15:31:17.640 回答