-- | A natural transformations between two 'Functor' instances. Law:---- > fmap f . eta g == eta g . fmap f---- Neat fact: the type system actually guarantees this law.--newtype f :-> g =Natural { eta :: forall x. f x -> g x }
listToMaybe :: [] :-> MaybelistToMaybe = Natural gowhere go [] = Nothinggo (x:_) = Just x
maybeToList :: Maybe :-> []maybeToList = Natural gowhere go Nothing = []go (Just x) = [x]
reverse' :: [] :-> []reverse' = Natural reverse
基本上,在Haskell中,自然转换是从某种类型f x到另一种类型g x的函数,使得x类型变量对调用者来说是“不可访问”的。所以例如,sort :: Ord a => [a] -> [a]不能变成自然转换,因为它对我们可以为a实例化哪些类型是“挑剔的”。我经常用一种直观的方式来思考这个问题:
仿函数是一种在不触及结构的情况下对内容进行操作的方法。
自然转换是一种在不接触或查看内容的情况下对结构进行操作的方式。
现在,把它拿出来,让我们来解决定义的条款。
第一个子句是“一个内函子,T: X->X”。嗯,Haskell中的每个Functor都是人们所说的“Hask类别”中的一个内函子,其对象是Haskell类型(类型*),其态射是Haskell函数。这听起来像是一个复杂的陈述,但实际上是一个非常琐碎的陈述。它的意思是Functor f :: * -> *为您提供了为任何a :: *构建类型f a :: * 的方法,以及从任何f :: a -> b中构建函数fmap f :: f a -> f b的方法,并且它们遵守仿函数定律。
第二个子句:Haskell中的Identity仿函数(平台附带,因此您可以导入它)是这样定义的:
newtype Identity a = Identity { runIdentity :: a }
instance Functor Identity wherefmap f (Identity a) = Identity (f a)
newtype Compose f g a = Compose { getCompose :: f (g a) }
-- | The composition of two 'Functor's is also a 'Functor'.instance (Functor f, Functor g) => Functor (Compose f g) wherefmap f (Compose fga) = Compose (fmap (fmap f) fga)
所以Tom Crockett定义的自然变换μ: T×T->T可以这样写:
join' :: Monad t => Compose t t :-> tjoin' = Natural (join . getCompose)
Mac Lane使用该描述来描述Monad的事实,有人可能暗示它描述了monad独有的东西。请原谅我。为了对该语句有更广泛的理解,我认为需要明确他是没有描述monad独有的东西;该语句同样描述了Application ative和Arrow等。出于同样的原因,我们可以在Int(Sum和Products)上有两个单参数,我们可以在X上有几个内函子类别的单参数。但相似性更多。
语句使用“…的类别”来定义语句的范围。例如,函数类别描述了f * -> g *的范围,即Any functor -> Any functor,例如Tree * -> List *或Tree * -> Tree *。
分类语句未指定的内容描述了任何事都是允许的的位置。
在这种情况下,在函子内部,没有指定* -> *,也就是a -> b,这意味着Anything -> Anything including Anything else。当我的想象力跳到Int->String时,它还包括Integer -> Maybe Int,甚至Maybe Double -> Either String Int,其中a :: Maybe Double; b :: Either String Int。
因此,该声明汇集如下:
仿函数作用域:: f a -> g b(即,任何参数化类型到任何参数化类型)
endo+仿函数:: f a -> f b(即,任何一个参数化类型到相同的参数化类型)…
内函子范畴中的一元半群
那么,这个构造的力量在哪里?为了欣赏完整的动态,我需要看到monot的典型图纸(看起来像身份箭头的单个对象,:: single object -> single object)未能说明我被允许使用由任何数字的monot值参数化的箭头,来自Monot中允许的一个类型对象。等价忽略的endo,~身份箭头定义,仿函数的类型值以及最内部“有效负载”层的类型和值。因此,在功能类型匹配的任何情况下,等价返回true(例如,Nothing -> Just * -> Nothing等价于Just * -> Just * -> Just *,因为它们都是Maybe -> Maybe -> Maybe)。
侧边栏:~外部是概念性的,但它是f a中最左边的符号。它也描述了“Haskell”首先读取的内容(大图);所以Type相对于类型值来说是“外部”。编程中层(引用链)之间的关系不容易在类别中联系起来。集合的类别用于描述类型(Int、Strings、也许Int等),其中包括函数的类别(参数化类型)。引用链:函数类型、函数值(函数集合的元素,例如,无、只是),以及每个函数值指向的其他所有东西。在类别中,关系的描述方式不同,例如,return :: a -> m a被认为是从一个函数到另一个函数的自然转换,与迄今为止提到的任何东西都不同。
这经常被误解。该陈述继续将join :: m (m a) -> m a描述为一元内函子的张量积。然而,它没有阐明在该陈述的上下文中,(<*>)也可以被选择。它确实是一个“六合一,半打合一”的例子。组合值的逻辑完全相同;相同的输入产生相同的输出(与Int的Sum和乘积单元格不同,因为它们在组合Ints时产生不同的结果)。
所以,总结一下:内函子范畴中的一元半群描述了:
~t :: m * -> m * -> m *and a neutral value for m *
(<*>)和(>>=)都提供对两个m值的同时访问,以便计算单个返回值。用于计算返回值的逻辑完全相同。如果不是因为它们参数化的函数形状不同(f :: a -> b对k :: a -> m b)以及参数的位置具有相同的计算返回类型(即分别为a -> b -> b对b -> a -> b),我怀疑我们可以参数化一元逻辑,张量积,以便在两个定义中重用。作为说明这一点的练习,尝试实现~t,你最终会得到(<*>)和(>>=),这取决于你决定如何定义它(>>=)0。