Hask的类别适合哪些值?

所以我们有Hask Category,其中:

  • 类型是类别的对象
  • 函数是类中从对象到对象的态射。
  • Functor类似,我们有:

  • 一个类型构造器,用于将对象从一个类别映射到另一个类别
  • 用于将态射从一个类别映射到另一个类别的fmap
  • 现在,当我们编写程序时,我们基本上转换了值(而不是类型),看起来Hask的类别根本不谈论值。 我试图在整个方程中拟合数值,并提出以下观察结果:

  • 每个类型本身就是一个类别。 例如:Int是所有整数的类别。
  • 从一个值到同一类型的另一个值的函数是该类别的态射。 例如: Int -> Int
  • 从一个值到另一个不同类型值的函数是用于将一个类型的值映射到另一个类型的函子。
  • 现在我的问题是 - Hask的范畴(或一般范畴理论)中的值是否有意义? 如果是,那么任何引用来阅读它,如果没有,那么任何理由。

    我希望这个问题很有意义:)


    (除非我mark it as code ,否则我将从数学/类别理论中使用含义的单词而非编程。)

    一次一个类别

    类别理论的一个重要思想是将大型复杂事物视为一个点,因此,当你想到类别Hask时,所有整数的set / group / ring / class / category都被认为是单一点。

    同样,你可以在整数上有一个非常复杂的函数,但它只是一个集合(set / class)态射的单个元素(点/箭头)。

    你在类别理论中做的第一件事就是忽略细节。 所以Hask这个类别并不在乎Int可以被认为是一个类别 - 这是一个不同的级别。 Int只是Hask中的一个点(对象)。

    一级关闭

    每个幺半群是一个具有一个对象的类别。 让我们使用它。

    整数如何是一个类?

    对此有多个答案(因为整数是加法下的幺半群,乘法下是幺半群)。 让我们来补充一下:

    你可以把整数视为一个单个对象的范畴,而态射是诸如(+1),(+2),(减4)之类的函数。

    你必须坚持认为我将整数7视为数字7,但使用表示(+7)使其看起来是一个类别。 类别理论的定律故意不要说你的态射必须是函数,但如果它具有包含身份和在合成下关闭的一组函数的结构,那么它就更清楚了。

    任何monoid都可以像我们刚刚完成整数一样来创建单个对象类别。

    整数的函子?

    函数f从整数作为操作下一个类别+ ,以某种其他类型的与操作£形成一个类别只能是一个算符,如果你有f(x+y) = f(x) £ f(y) 。 (这被称为幺半同态)。 大多数函数不是态射。

    示例态度

    String s是++下的monoid,所以它们是一个类。

    len :: String -> Int
    len = length
    

    len是一个从StringInt的单态Int ,因为len (xs ++ ys) = len xs + len ys ,所以如果你考虑( String++ )和( Int+ )作为范畴, len是一个仿函数。

    示例非态射

    Bool|| )是一个monoid,以False为标识,所以它是一个单一对象类别。 功能

    quiteLong :: String -> Bool
    quiteLong xs = length xs > 10
    

    不是一种态度quiteLong "Hello "因为quiteLong "Hello "FalsequiteLong "there!" 也是False ,但是quiteLong ("Hello " ++ "there!")True ,而且是False || False False || False不是True

    因为quiteLong不是一个态射体,它也不是一个函子。

    你有什么意见,安德鲁?

    我的观点是, 一些 Haskell类型可以被视为类别,但并不是所有的函数都是morhpisms。

    我们不会同时考虑不同级别的类别(除非您将这两个类别用于某种奇怪的目的),并且故意没有这些级别之间的理论交互,因为故意没有关于对象和态射的详细信息。

    这部分是因为范畴理论在数学中起了一定的作用,它提供了一种语言来描述伽罗瓦理论在有限群/子群和场/场扩展之间可爱的相互作用,这两种看起来完全不同的结构变得密切相关。 后来,同源/同伦理论在拓扑空间和组之间产生了函数,这两个函数变得既迷人又有用,但重点是允许对象和态射在两类函数中彼此非常不同。

    (通常,类别理论以哈斯克函数的形式进入哈斯克尔,所以在函数式编程的实践中,这两类是相同的。)

    那么...原始问题的答案究竟是什么?

  • 每个类型本身就是一个类别。 例如:Int是所有整数的类别。
  • 如果你以特定的方式思考它们。 有关详细信息,请参阅PhilipJF的答案。

  • 从一个值到同一类型的另一个值的函数是该类别的态射。 例如: Int -> Int
  • 我认为你混淆了这两个层次。 函数可以是Hask中的态射,但不是所有函数Int -> Int都是加法结构下的函数,例如fx = 2 * x + 10不是Int和Int之间的函数,所以它不是类别态射(另一种方式( Int+ )到( Int+ ),但它是一个态射Int -> Int在类别Hask中。

  • 从一个值到另一个不同类型值的函数是用于将一个类型的值映射到另一个类型的函子。
  • 不,不是所有的函数都是函数,比如quiteLong不是。

    Hask的范畴(或一般范畴理论)甚至有价值吗?

    类别在类别理论中没有值,它们只有对象和态射,它们被视为顶点和有向边。 对象不一定有价值观,价值观不属于类别理论。


    正如我评论过安德鲁的答案(这是非常好的答案),您可以将类型中的值作为该类型的对象作为类别,并将函数看作函子。 为了完整性,这里有两种方法:

    设置为无聊类别

    数学中最常用的工具之一就是“setoid” - 也就是说,它是一个具有等价关系的集合。 我们可以通过“groupoid”的概念来明确地考虑这一点。 群体是一个类别,每一个态射都有一个倒数,使得f . (inv f) = id f . (inv f) = id(inv f) . f = id (inv f) . f = id

    为什么这会捕捉等价关系的想法? 那么,一个等价关系必须是自反的,但这只是它具有同一性箭头的绝对主张,并且它必须是传递性的,但这只是组合,最后它需要是对称的(这就是我们添加反向的原因)。

    因此,任何一组数学上的平等概念因此产生一个类群结构:即唯一的箭头就是同一个箭头! 这通常被称为“离散类别”。

    作为练习,读者可以证明所有函数都是离散类别之间的函子。

    如果你认真对待这个想法,你会开始怀疑那些不仅仅是身份的“平等”。 这将允许我们编码“商数类型”。 更重要的是,类群结构还有更多的公理(联想性等),它们是关于“平等证明的平等”的主张,它导致了n群体和更高范畴理论的道路。 这是很酷的东西,虽然要有用,你需要依赖类型,有些还没有完全算出来,当它最终使它成为编程语言时,应该允许

    data Rational where
        Frac :: Integer -> Integer -> Rational
        SameRationa :: (a*d) ~ (b*c) -> (Frac a b) ~ (Frac c d)
    

    这样,每当你进行图案匹配时,你也必须匹配额外的等式公理,从而证明你的函数尊重了Rational的等价关系,但不要担心这一点。 带走只是“离散类别”的解释是非常好的解释。

    指示方法

    Haskell中的每种类型都有一个额外的值,即undefined值。 这是怎么回事? 那么,我们可以在每种类型上定义一个关于如何“定义”一个值的偏序,例如

    forall a. undefined <= a
    

    而且还有类似的东西

    forall a a' b b'. (a <= a') / (b <= b') -> ((a,b) <= (a',b'))
    

    未定义的定义较少,因为它指的是不会终止的值(实际上, undefined函数是通过在每个haskell中抛出一个异常来实现的,但假设它是undefined = undefined 。你不能确定某个东西没有终止如果给你一个undefined你所能做的就是等着瞧,因此,它可能是任何东西。

    部分订单以标准方式产生类别。

    因此,每种类型都会以这种方式产生一个类别,其中的值是对象。

    为什么是函数仿函数? 嗯,一个函数不能说因为暂停问题而得到一个undefined的函数。 因此,它在遇到一个时必须回到undefined ,或者无论它给出了什么,它都必须得到相同的答案。 这是留给你,以表明真的是一个函子。


    虽然还有一些其他的,非常精彩的答案,但他们在一定程度上会错过你的第一个问题。 要清楚,价值根本不存在,在Hask类别中没有意义。 这不是哈斯克可以谈论的。

    上面的说法或感觉似乎有点愚蠢,但是我提出它很重要,因为重要的是要注意,类别理论只提供了一个镜头,用于检查像编程语言那样复杂的复杂交互和结构。 期望所有这种结构都归入一个类别的相当简单的概念,这并不是富有成效的。 [1]

    另一种说法是,我们试图分析一个复杂的系统,为了找出有趣的模式,将它视为一个类别有时很有用。 正是这种思维方式让我们介绍Hask,检查它是否确实形成了一个类别,注意到Maybe看起来像一个Functor,然后使用所有这些机制来记录一致性条件。

    fmap id = id
    fmap f . fmap g = fmap (f . g)
    

    无论我们是否引入Hask,这些规则都是有意义的,但通过将它们看作是一个简单结构的简单后果,我们可以在Haskell中发现它,我们理解它们的重要性。


    作为技术说明,这个答案的全部假定Hask实际上是“柏拉图式的”Hask,即我们可以尽可能多地忽略底部( undefined和非终止)。 没有这些,几乎整个争论就会分崩离析。


    让我们仔细研究这些定律,因为它们看起来几乎违背了我的初始陈述 - 它们显然是在价值层面上运作的,但“哈斯克中并不存在价值”,对吧?

    那么,一个答案就是仔细观察一个分类仿函数是什么。 显然,它是两个类别(比如C和D)之间的映射,它将C的对象带到D的对象,C的箭头指向D的箭头。值得注意的是,通常这些“映射”不是分类箭头 - 它们只是形成类别之间的关系并不一定与类别具有结构上的一致性。

    这很重要,因为即使考虑Haskell的Haskell Functor s,endofunctors,我们也必须小心。 在Hask中,对象是Haskell类型,箭头是这些类型之间的Haskell函数。

    再来看看Maybe 。 如果它将成为Hask的一名执行官,那么我们需要一种方法将Hask中的所有类型都带到Hask中的其他类型中。 这个映射不是一个Haskell函数,即使它可能像一个: pure :: a -> Maybe a不符合条件,因为它在值级别运行。 相反,我们的对象映射是Maybe本身:对于任何类型a我们可以形成类型Maybe a

    这已经突出了在没有值的情况下在Hask中工作的价值---我们确实想要分离出不依赖pureFunctor的概念。

    我们将通过检查我们的Maybe endofunctor的箭头映射来开发Functor的其余部分。 在这里,我们需要一种方法将Hask的箭头映射到Hask的箭头。 现在让我们假设这不是一个Haskell函数 - 它不一定是 - 所以为了强调它,我们将以不同的方式写它。 如果f是一个Haskell函数a -> b那么Maybe [ f ]是一些其他的Haskell函数Maybe a -> Maybe b

    现在,很难不跳过,只是开始调用Maybe [ f ]“ fmap f ”,但我们可以做更多的工作,然后再进行跳转。 也许[ f ]需要有一定的连贯条件。 特别是,对于任何类型的a在Hask我们有一个id箭头。 在我们的元语言中,我们可以将它称为id [ a ],并且我们知道它也通过Haskell名称id :: a -> a 。 总而言之,我们可以用这些来说明工作人员一致性条件:

    对于Hask a所有对象,我们有这个Maybe [id [ a ]] = id [ Maybe a ]。 对于Hask fg任何两个箭头,我们都有那个Maybe [ f . g f . g ] =可能[ f ]。 也许[ g ]。

    最后一步是注意Maybe [_]恰好可以作为Haskell函数本身作为Hask对象全部forall ab . (a -> b) -> (Maybe a -> Maybe b)的值来实现forall ab . (a -> b) -> (Maybe a -> Maybe b) forall ab . (a -> b) -> (Maybe a -> Maybe b) 。 这给了我们Functor


    尽管上述内容相当复杂且技术性强,但将Hask和分类内涵工具的概念与Haskell实例化保持直接分离还有一点很重要。 特别是,我们可以发现所有这种结构,而不需要fmap作为真正的Haskell函数存在。 哈斯克是一个没有在价值层面引入任何东西的类别。

    这就是Hask作为一个类别观看的真正跳动心脏。 用Functor标识Hask的endofunctors的符号需要更多的线条模糊。

    这种模糊是合理的,因为Hask有指数。 这是一种棘手的方式,它说整个分类箭头与Hask中特定的特殊对象之间存在统一。

    为了更加明确,我们知道对于Hask的任何两个对象,比如ab ,我们可以讨论这两个对象之间的箭头,通常表示为Hask( ab )。 这只是一个数学集合,但我们知道Hask中有另一种与Hask( ab )密切相关的类型: (a -> b)

    所以这很奇怪。

    我最初宣称一般Haskell值在Hask的分类表示中绝对没有表示。 然后我继续证明,我们可以用Hask做很多事情,只使用它的分类概念,而不是将Haskell内部的那些片段作为值进行粘贴。

    但是现在我注意到,类似于a -> b的类型的值实际上确实存在于元语言集合Hask( ab )中的所有箭头。 这真是一个窍门,正是这种元语言模糊使得指数类别如此有趣。

    不过,我们可以做得更好一点! 哈斯克也有一个终端对象。 我们可以通过调用0来谈论这个元语言,但我们也知道它是Haskell类型() 。 如果我们查看Hask对象a我们知道Hask( ()a )中有一整套分类箭头。 此外,我们知道这些对应于类型() -> a 。 最后,由于我们知道给定任何函数f :: () -> a我们可以通过应用()立即得到一个a ,可能想要说Hask( ()a )中的分类箭头恰好是Haskell的值键入a

    这应该是完全混乱或令人难以置信的令人兴奋的。


    通过坚持我的最初陈述,我将在哲学上有点结束:哈斯克根本不谈论哈斯克尔的价值。 它确实不是一个纯粹的类别---分类是有趣的,正是因为他们是非常简单的,因此并不需要的类型和值以及所有这些多余的,明确的概念typeOf包容等。

    但是,我也许很糟糕地表明,即使是一个严格的类别,Hask也有一些与Haskell的所有值非常相似的东西:Hask( ()a )的箭头,用于每个Hask对象a

    在哲学上,我们可能会争辩说,这些箭头并不是我们正在寻找的Haskell值 - 它们仅仅是替身,绝对的嘲讽。 你可能会争辩说他们是不同的东西,但只是碰巧与Haskell值一一对应。

    我其实认为这是一个非常重要的想法。 这两件事情是不同的,他们只是表现得很相似。


    非常相似。 任何类别都可以让你编写箭头,所以我们假设我们在Hask( ab )和Hask( ()a )中选择了一些箭头。 如果我们将这些箭头与类别组合相结合,我们将在Hask( ()b )中获得一个箭头。 把它全部放在头上,我们可以说我刚刚做的是找到一个类型a -> b的值,类型a的值,然后将它们组合得到类型b的值。

    换句话说,如果我们侧身观察事物,我们可以将分类箭头组合看作函数应用的一般形式。

    这就是Hask这样的分类如此有趣的原因。 大致来说,这些类别被称为笛卡尔封闭类别或CCC。 由于既有初始对象也有指数(它们也需要产品),它们具有完全建模类型lambda微积分的结构。

    但他们仍然没有价值。

    [1]如果您在阅读我的答案的其余部分之前阅读本文,请继续阅读。 事实证明,虽然期望这种情况发生是荒谬的,但实际上它确实是这样。 如果您在阅读我的整个答案后阅读本文,那么让我们来反思CCC的酷炫程度。

    链接地址: http://www.djcxy.com/p/43311.html

    上一篇: Where do values fit in Category of Hask?

    下一篇: How are functors in Haskell related to functors in category theory?