我想提出一种更系统的方法来回答这个问题,并展示不使用任何特殊技巧的示例,例如“底部”值或无限数据类型或类似的东西。
类型构造函数什么时候没有类型类实例?
通常,类型构造函数无法拥有某个类型类的实例有两个原因:
- 无法从类型类实现所需方法的类型签名。
- 可以实现类型签名但不能满足所需的规律。
第一种的例子比第二种容易,因为第一种,我们只需要检查一个给定类型签名的函数是否可以实现,而第二种,我们需要证明没有实现可能会满足法律。
具体例子
对于类型参数a
,这是一个反函子,而不是函子,因为a
它处于逆变位置。不可能实现具有类型签名的函数(a -> b) -> F z a -> F z b
。
即使fmap
可以实现的类型签名,也不是合法函子的类型构造函数:
data Q a = Q(a -> Int, a)
fmap :: (a -> b) -> Q a -> Q b
fmap f (Q(g, x)) = Q(\_ -> g x, f x) -- this fails the functor laws!
这个例子的奇怪之处在于我们可以实现fmap
正确的类型,即使F
不可能是函子,因为它使用a
在逆变位置。因此,上面显示的这种实现具有fmap
误导性——即使它具有正确的类型签名(我相信这是该类型签名的唯一可能实现),但函子定律并不满足。例如,fmap id
≠ id
,因为let (Q(f,_)) = fmap id (Q(read,"123")) in f "456"
是123
,但是let (Q(f,_)) = id (Q(read,"123")) in f "456"
是456
。
事实上,F
它只是一个函子,它既不是函子也不是反函子。
一个不适用的合法函子,因为 的类型签名pure
无法实现:采用 Writer monad(a, w)
并删除w
应该是 monoid 的约束。那么就不可能构造出一个类型为 的(a, w)
值a
。
一个不适用的函子,<*>
因为无法实现的类型签名: data F a = Either (Int -> a) (String -> a)
。
即使可以实现类型类方法,也不能合法应用的仿函数:
data P a = P ((a -> Int) -> Maybe a)
类型构造函数P
是一个仿函数,因为a
它只在协变位置使用。
instance Functor P where
fmap :: (a -> b) -> P a -> P b
fmap fab (P pa) = P (\q -> fmap fab $ pa (q . fab))
的类型签名的唯一可能实现<*>
是始终返回的函数Nothing
:
(<*>) :: P (a -> b) -> P a -> P b
(P pfab) <*> (P pa) = \_ -> Nothing -- fails the laws!
但是这个实现不满足应用函子的恒等律。
- 一个函子,
Applicative
但不是,Monad
bind
因为无法实现的类型签名。
我不知道任何这样的例子!
- 一个函子,
Applicative
但不是 aMonad
,因为即使bind
可以实现 的类型签名,也无法满足法律。
这个例子引起了相当多的讨论,所以可以肯定地说,证明这个例子是正确的并不容易。但有几个人通过不同的方法独立验证了这一点。请参阅是否`数据 PoE a = 空 | 将 aa` 与 monad 配对?进行额外讨论。
data B a = Maybe (a, a)
deriving Functor
instance Applicative B where
pure x = Just (x, x)
b1 <*> b2 = case (b1, b2) of
(Just (x1, y1), Just (x2, y2)) -> Just((x1, x2), (y1, y2))
_ -> Nothing
证明没有合法的Monad
事例有点麻烦。非单子行为的原因是没有自然的方式来实现bind
函数何时f :: a -> B b
可以返回Nothing
或Just
不同的a
.
考虑一下可能更清楚Maybe (a, a, a)
,这也不是单子,并尝试join
为此实现。一会发现并没有直观合理的实现方式join
。
join :: Maybe (Maybe (a, a, a), Maybe (a, a, a), Maybe (a, a, a)) -> Maybe (a, a, a)
join Nothing = Nothing
join Just (Nothing, Just (x1,x2,x3), Just (y1,y2,y3)) = ???
join Just (Just (x1,x2,x3), Nothing, Just (y1,y2,y3)) = ???
-- etc.
在 指出的情况下???
,很明显我们不能Just (z1, z2, z3)
以任何合理和对称的方式从六个不同的类型值中产生a
。我们当然可以选择这六个值的任意子集——例如,总是取第一个非空值Maybe
——但这不满足单子定律。回归Nothing
也不会满足法律。
- 一种树状数据结构,它不是单子,即使它具有关联性,
bind
但不符合恒等律。
通常的树状单子(或“具有函子形分支的树”)定义为
data Tr f a = Leaf a | Branch (f (Tr f a))
这是函子上的一个自由单子f
。数据的形状是一棵树,其中每个分支点都是子树的“函子”。标准二叉树将通过 获得type f a = (a, a)
。
如果我们修改这个数据结构,也把叶子做成函子的形状f
,我们得到我所说的“半单子”——它bind
满足自然性和结合律,但它的pure
方法不符合恒等律之一。“半单子是内函子范畴中的半群,有什么问题?” 这是类型类Bind
。
为简单起见,我定义了join
方法而不是bind
:
data Trs f a = Leaf (f a) | Branch (f (Trs f a))
join :: Trs f (Trs f a) -> Trs f a
join (Leaf ftrs) = Branch ftrs
join (Branch ftrstrs) = Branch (fmap @f join ftrstrs)
枝条嫁接是标准的,而叶子嫁接是非标准的,并产生Branch
. 这对结合律来说不是问题,但违反了恒等律之一。
多项式类型什么时候有单子实例?
这两个函子Maybe (a, a)
和Maybe (a, a, a)
都不能给出合法的Monad
实例,尽管它们显然是Applicative
。
这些函子没有技巧——没有Void
或bottom
任何地方,没有棘手的惰性/严格性,没有无限的结构,也没有类型类约束。该Applicative
实例是完全标准的。这些函子的功能return
和bind
可以实现,但不满足单子法则。换句话说,这些函子不是单子,因为缺少特定的结构(但要理解到底缺少什么并不容易)。举个例子,函子中的一个小改动就可以使它变成一个 monad:data Maybe a = Nothing | Just a
是一个 monad。另一个类似的函子data P12 a = Either a (a, a)
也是一个单子。
多项式单子的构造
一般来说,这里有一些Monad
从多项式类型中产生合法 s 的结构。在所有这些结构中,M
是一个 monad:
type M a = Either c (w, a)
w
任何幺半群在哪里
type M a = m (Either c (w, a))
哪里m
是任何单子并且w
是任何幺半群
type M a = (m1 a, m2 a)
哪里m1
有m2
任何单子
type M a = Either a (m a)
m
单子在哪里
第一个构造是WriterT w (Either c)
,第二个构造是WriterT w (EitherT c m)
。第三种构造是 monads 的组件式乘积:pure @M
定义为 和 的组件式乘积,并pure @m1
通过省略叉积数据定义(例如,通过省略元组的第二部分映射到):pure @m2
join @M
m1 (m1 a, m2 a)
m1 (m1 a)
join :: (m1 (m1 a, m2 a), m2 (m1 a, m2 a)) -> (m1 a, m2 a)
join (m1x, m2x) = (join @m1 (fmap fst m1x), join @m2 (fmap snd m2x))
第四种构造定义为
data M m a = Either a (m a)
instance Monad m => Monad M m where
pure x = Left x
join :: Either (M m a) (m (M m a)) -> M m a
join (Left mma) = mma
join (Right me) = Right $ join @m $ fmap @m squash me where
squash :: M m a -> m a
squash (Left x) = pure @m x
squash (Right ma) = ma
我已经检查了所有四个结构都产生了合法的单子。
我猜想多项式单子没有其他结构。例如,函子Maybe (Either (a, a) (a, a, a, a))
不是通过任何这些结构获得的,因此不是一元的。但是,是单子的,因为它与三个单子、和Either (a, a) (a, a, a)
的乘积同构。此外,是一元的,因为它与 和 的乘积同构。a
a
Maybe a
Either (a,a) (a,a,a,a)
a
Either a (a, a, a)
上面显示的四种结构将允许我们获得任意数量的任意乘积的任意总和,a
例如Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
等等。所有此类类型构造函数都将具有(至少一个)Monad
实例。
当然,这些单子可能存在哪些用例还有待观察。另一个问题是Monad
通过构造 1-4 派生的实例通常不是唯一的。例如,type F a = Either a (a, a)
可以通过Monad
两种方式给类型构造函数一个实例:构造 4 使用 monad (a, a)
,构造 3 使用类型 isomorphism Either a (a, a) = (a, Maybe a)
。同样,找到这些实现的用例并不是很明显。
一个问题仍然存在 - 给定任意多项式数据类型,如何识别它是否有Monad
实例。我不知道如何证明多项式单子没有其他结构。我认为到目前为止还没有任何理论可以回答这个问题。