对于某些类型F a
,某些Haskell仿函数T -> a
显然与T
同构,例如。
data Pair a = Pair a a -- isomorphic to Bool -> a
data Reader r a = Reader (r -> a) -- isomorphic to r -> a (duh!)
data Identity a = Identity a -- isomorphic to () -> a
data Phantom a = Phantom -- isomorphic to void -> a
(这些同构仅仅是严格的,只考虑有限的数据结构。)
一般来说,我们怎样才能在可能的情况下描述仿函数?
问题是“哪个Haskell Functors可以表示?”同样的问题?
答案 0 :(得分:19)
诺亚对动物说:“出去繁衍!”但是蛇说 “我们不能成倍增加,因为我们是加法器。”所以诺亚从方舟中取出木材, 塑造它,说:“我正在为你建造一张原木桌。”。
可代表的仿函数有时也被称为“Naperian”仿函数(它是Peter Hancock的术语:Hank是爱丁堡与约翰纳皮尔同一部分的居民,具有对数名声),因为当F x ~= T -> x
,并且记住,组合时, T -> x
是“x
强权T
”,我们发现T
在某种意义上是Log F
。
首先要注意的是F () ~= T -> () ~= ()
。这告诉我们只有一个形状。为我们提供形状选择的函数不能是Naperian,因为它们不能统一呈现数据的位置。这意味着[]
不是Naperian,因为不同长度的列表具有由不同类型表示的位置。但是,无限Stream
具有由自然数给出的位置。
相应地,给定任何两个F
结构,它们的形状必然匹配,因此它们具有合理的zip
,为我们提供Applicative F
实例的基础。
确实,我们有
a -> p x
=====================
(Log p, a) -> x
使p
成为正确的伴随,因此p
保留所有限制,特别是单位和产品,使其成为一个幺正的函子,而不仅仅是 lax monoidal仿函数。也就是说,Applicative
的替代表示具有同构的操作。
unit :: () ~= p ()
mult :: (p x, p y) ~= p (x, y)
让我们有一个类型的东西。我的烹饪方法与Representable
课程略有不同。
class Applicative p => Naperian p where
type Log p
logTable :: p (Log p)
project :: p x -> Log p -> x
tabulate :: (Log p -> x) -> p x
tabulate f = fmap f logTable
-- LAW1: project logTable = id
-- LAW2: project px <$> logTable = px
我们有Log f
类型,至少代表f
内的部分位置;我们有一个logTable
,在每个位置存储该位置的代表,就像每个地方都有地名的“f
”地图一样;我们有project
函数提取存储在给定位置的数据。
第一定律告诉我们logTable
对于所代表的所有位置都是准确的。第二定律告诉我们,我们已经代表所有这些职位。我们可以推断出
tabulate (project px)
= {definition}
fmap (project px) logTable
= {LAW2}
px
那个
project (tabulate f)
= {definition}
project (fmap f logTable)
= {free theorem for project}
f . project logTable
= {LAW1}
f . id
= {composition absorbs identity}
f
我们可以设想Applicative
instance Naperian p => Applicative p where
pure x = fmap (pure x) logTable
pf <$> px = fmap (project pf <*> project ps) logTable
这就是说p
从通常的K和S继承了自己的K和S组合子的功能。
当然,我们有
instance Naperian ((->) r) where
type Log ((->) r) = r -- log_x (x^r) = r
logTable = id
project = ($)
现在,所有类似限制的结构都保留了Naperianity。 Log
将限制性事物映射到集体事物:它计算左边邻接。
我们有终端对象和产品。
data K1 x = K1
instance Applicative K1 where
pure x = K1
K1 <*> K1 = K1
instance Functor K1 where fmap = (<*>) . pure
instance Naperian K1 where
type Log K1 = Void -- "log of 1 is 0"
logTable = K1
project K1 nonsense = absurd nonsense
data (p * q) x = p x :*: q x
instance (Applicative p, Applicative q) => Applicative (p * q) where
pure x = pure x :*: pure x
(pf :*: qf) <*> (ps :*: qs) = (pf <*> ps) :*: (qf <*> qs)
instance (Functor p, Functor q) => Functor (p * q) where
fmap f (px :*: qx) = fmap f px :*: fmap f qx
instance (Naperian p, Naperian q) => Naperian (p * q) where
type Log (p * q) = Either (Log p) (Log q) -- log (p * q) = log p + log q
logTable = fmap Left logTable :*: fmap Right logTable
project (px :*: qx) (Left i) = project px i
project (px :*: qx) (Right i) = project qx i
我们有身份和构成。
data I x = I x
instance Applicative I where
pure x = I x
I f <*> I s = I (f s)
instance Functor I where fmap = (<*>) . pure
instance Naperian I where
type Log I = () -- log_x x = 1
logTable = I ()
project (I x) () = x
data (p << q) x = C (p (q x))
instance (Applicative p, Applicative q) => Applicative (p << q) where
pure x = C (pure (pure x))
C pqf <*> C pqs = C (pure (<*>) <*> pqf <*> pqs)
instance (Functor p, Functor q) => Functor (p << q) where
fmap f (C pqx) = C (fmap (fmap f) pqx)
instance (Naperian p, Naperian q) => Naperian (p << q) where
type Log (p << q) = (Log p, Log q) -- log (q ^ log p) = log p * log q
logTable = C (fmap (\ i -> fmap (i ,) logTable) logTable)
project (C pqx) (i, j) = project (project pqx i) j
Naperian仿函数在最大修复点下关闭,其对数是相应的最小修正点。例如,对于流,我们有
log_x (Stream x)
=
log_x (nu y. x * y)
=
mu log_xy. log_x (x * y)
=
mu log_xy. log_x x + log_x y
=
mu log_xy. 1 + log_xy
=
Nat
在Haskell中渲染它而不引入Naperian bifunctors (其中有两组位置用于两种情况)或(更好)Naperian仿函数在索引类型(已编制索引)上有点繁琐索引事物的位置)。但是,有趣的是,并且希望能够提出这个想法,这就是cofree comonad。
data{-codata-} CoFree p x = x :- p (CoFree p x)
-- i.e., (I * (p << CoFree p)) x
instance Applicative p => Applicative (CoFree p) where
pure x = x :- pure (pure x)
(f :- pcf) <*> (s :- pcs) = f s :- (pure (<*>) <*> pcf <*> pcs)
instance Functor p => Functor (CoFree p) where
fmap f (x :- pcx) = f x :- fmap (fmap f) pcx
instance Naperian p => Naperian (CoFree p) where
type Log (CoFree p) = [Log p] -- meaning finite lists only
logTable = [] :- fmap (\ i -> fmap (i :) logTable) logTable
project (x :- pcx) [] = x
project (x :- pcx) (i : is) = project (project pcx i) is
我们可以Stream = CoFree I
,给予
Log Stream = [Log I] = [()] ~= Nat
现在,仿函数的导数D p
给出了它的单孔上下文类型,告诉我们i)p
的形状,ii)孔的位置,iii)数据那不在洞里。如果p
是Naperian,则没有形状选择,因此将非常重要的数据放在非孔位置,我们发现只是得到了孔的位置。
D p () ~= Log p
有关该连接的更多信息,请参阅this answer of mine有关尝试的内容。
无论如何,Naperian对于Representable来说确实是一个有趣的本地苏格兰名字,你可以建立一个日志表:它们是完全由投影构成的结构,不提供“形状”选择。