我想提出一种更系统的方法来回答这个问题,并展示不使用任何特殊技巧的示例,例如“底部”值或无限数据类型或类似的东西。
类型构造函数什么时候没有类型类实例?
一般来说,类型构造函数无法拥有某个类型类的实例有两个原因:
- 无法从类型类中实现所需方法的类型签名。
- 可以实现类型签名但不能满足要求的规律。
第一种的例子比第二种容易,因为第一种,我们只需要检查是否可以实现一个给定类型签名的函数,而第二种,我们需要证明没有任何实施可能满足法律要求。
具体例子
对于类型参数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 应该是半体。那么就不可能从a 中构造一个(a, w) 类型的值。
一个不适用的函子,因为无法实现<*> 的类型签名: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,但不是Monad,因为即使可以实现bind 的类型签名,也无法满足规律。
这个例子引起了相当多的讨论,所以可以肯定地说,证明这个例子是正确的并不容易。但有几个人通过不同的方法独立验证了这一点。更多讨论请参见Is `data PoE a = Empty | Pair a a` a 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实例有点麻烦。非单子行为的原因是,当函数f :: a -> B b 可以针对a 的不同值返回Nothing 或Just 时,没有自然的方式来实现bind。
考虑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.
在??? 指出的情况下,很明显我们不能以任何合理和对称的方式从a 类型的六个不同值中产生Just (z1, z2, z3)。我们当然可以选择这六个值的任意子集——例如,总是取第一个非空的Maybe——但这不满足单子定律。返回Nothing 也不符合法律规定。
-
不是 monad 的树状数据结构,尽管它具有
bind 的关联性 - 但不符合恒等律。
通常的树状 monad(或“具有函子形分支的树”)定义为
data Tr f a = Leaf a | Branch (f (Tr f a))
这是函子 f 上的免费 monad。数据的形状是一棵树,其中每个分支点都是子树的“函子”。标准二叉树将通过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。这对结合律来说不是问题,但违反了恒等律之一。
多项式类型什么时候有monad实例?
仿函数Maybe (a, a) 和Maybe (a, a, a) 都不能被赋予合法的Monad 实例,尽管它们显然是Applicative。
这些函子没有任何技巧——在任何地方都没有Void 或bottom,没有棘手的惰性/严格性,没有无限结构,也没有类型类约束。 Applicative 实例是完全标准的。函数return 和bind 可以为这些函子实现,但不满足单子定律。换句话说,这些函子不是 monad,因为缺少特定的结构(但很难理解到底缺少了什么)。例如,函子中的一个小变化可以使它成为一个 monad:data Maybe a = Nothing | Just a 是一个 monad。另一个类似的仿函数 data P12 a = Either a (a, a) 也是一个 monad。
多项式单子的构造
一般来说,这里有一些从多项式类型中产生合法Monads 的结构。在所有这些结构中,M 是一个单子:
-
type M a = Either c (w, a) 其中w 是任何幺半群
-
type M a = m (Either c (w, a)) 其中m 是任何monad,w 是任何monoid
-
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)。第三种构造是 monad 的组件式乘积: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 实例。
当然,这些单子可能存在哪些用例还有待观察。另一个问题是通过构造 1-4 派生的 Monad 实例通常不是唯一的。例如,类型构造函数type F a = Either a (a, a) 可以通过两种方式获得Monad 实例:构造4 使用单子(a, a),构造3 使用类型同构Either a (a, a) = (a, Maybe a)。同样,找到这些实现的用例并不是很明显。
问题仍然存在 - 给定任意多项式数据类型,如何识别它是否具有 Monad 实例。我不知道如何证明多项式单子没有其他结构。我认为目前还没有任何理论可以回答这个问题。