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

Cli*_*ton 6 haskell category-abstractions

我有一种想法可以($)像通用一样Control.Category概括(.),我已经用这篇文章末尾的代码(也是ideone)完成了.

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

($) :: f a b -> a -> b
Run Code Online (Sandbox Code Playgroud)

当然,我创建(->)了这个类的实例,因此$继续使用普通函数.

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

我总结说有三种可能性之一:

  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
Run Code Online (Sandbox Code Playgroud)

lef*_*out 10

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

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

class Category c => WellPointed c where
  type Terminal c :: *
  point :: a -> Terminal c `c` a
  unpoint :: Terminal c `c` a -> a
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过定义应用程序运算符

($) :: WellPointed c => c a b -> a -> b
f $ p = unpoint $ f . point p
Run Code Online (Sandbox Code Playgroud)

Hask本身WellPointed就是明显的例子:

instance WellPointed (->) where
  type Terminal c = ()
--point :: a -> () -> a
  point a () = a
--unpoint :: (() -> a) -> a
  unpoint f = f ()
Run Code Online (Sandbox Code Playgroud)

其他知名的范畴,Kleisli不是的一个实例WellPointed,因为我写的(它可以point,但不是unpoint).但是WellPointed如果它们可以在Haskell中正确实现的话,有很多类别可以成为一个好的实例.基本上,具有特定属性的所有数学函数类别(Lin K,Grp,{{•},Top } ......).这些不能直接表达的原因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)
Run Code Online (Sandbox Code Playgroud)

如您所见,类接口实际上与我上面写的有点不同.在Haskell中没有一种普遍接受的实现这种东西的方法,但是constrained-categories,$操作员实际上更像是Cirdec所描述的.


Cir*_*dec 5

在Haskell中有两个用于此类事物的抽象,一个使用Arrows和另一个Applicatives.两者都可以分解成比使用的更小的部分base.


如果你Arrow朝着这个方向前进并将s 的功能细分Arrow为组件,那么你就可以为那些能够将任意函数提升到箭头中的箭头分别设置一个类.

class ArrowArr a where
    arr :: (b -> c) -> a b c
Run Code Online (Sandbox Code Playgroud)

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

class ArrowFun a where
    ($) :: a b c -> (b -> c)
Run Code Online (Sandbox Code Playgroud)

如果你刚刚arr离开,Arrow你就会留下类似于可以构造和解构元组的类别的箭头.

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')
Run Code Online (Sandbox Code Playgroud)

如果你Applicative朝这个方向前进,那就是Copointed" Applicative没有pure"(这就是名字Apply).

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

class Functor f => Apply f where
  (<.>) :: f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

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