Haskell “理解”;单子m";在>&燃气轮机=
查看Haskell的绑定:Haskell “理解”;单子m";在>&燃气轮机=,haskell,Haskell,查看Haskell的绑定: Prelude> :t (>>=) (>>=) :: Monad m => m a -> (a -> m b) -> m b 我被下面的例子弄糊涂了: Prelude> let same x = x Prelude> [[1]] >>= \x -> same x [1] 查看>=的签名,\x->same x如何使用a->mb进行类型检查 我本以为\x->same x会产生一个[b
Prelude> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
我被下面的例子弄糊涂了:
Prelude> let same x = x
Prelude> [[1]] >>= \x -> same x
[1]
查看>=
的签名,\x->same x
如何使用a->mb
进行类型检查
我本以为\x->same x
会产生一个[b]
类型,因为我理解这里的单子m
类型是[]
我本以为\x->same x
会产生[b]
类型,因为据我所知,这里的单子m
类型是[]
它之所以如此,是因为它确实如此
我们有
[[1]] >>= \ x -> same x
=
[[1]] >>= \ x -> x
[[Int]] [Int] -> [Int] :: [Int]
[] [Int] [Int] -> [] Int :: [] Int
m a a m b m b
有时,
[]
描述了一种“不确定性”效应。其他时候,[]
正在描述类似容器的数据结构。事实上,很难区分这两个目的中的哪一个得到了满足,这是一些人非常自豪的一个特点。我不准备同意他们的观点,但我知道他们在做什么。这里的单子是[a]
,这个例子毫无意义地复杂。这会更清楚:
Prelude> [[1]] >>= id
[1]
正如
Prelude> [[1]] >>= const [2]
[2]
i、 e.
>=
是concatMap
,与id
一起使用时是concat
,正如您所说m
是[]
。然后a
是[Integer]
(为了简单起见,忽略数字是多态的这一事实),而b
是整数。因此a->mb
首先变成[Integer]->[Integer]
我们应该使用标准版本的相同的
,它被称为id
现在,让我们重命名一些类型变量
id :: (a'' ~ a) => a -> a''
这意味着:id
的签名是两种类型之间的函数映射的签名,额外的约束是两种类型相等。仅此而已–我们不需要任何特殊属性,如“平坦”
我为什么要这样写?如果我们也重命名绑定签名中的一些变量
(>>=) :: (Monad m, a'~m a, a''~m b) => a' -> (a -> a'') -> a''
…很明显,我们可以插入id
,因为类型变量已经被相应地命名了。来自id
的类型相等约束a'>a
被简单地带到化合物的签名,即
(>>=id) :: (Monad m, a'~m a, a''~m b, a''~a) => a' -> a''
或者,简化一下
(>>=id) :: (Monad m, a'~m a, m b~a) => a' -> m b
(>>=id) :: (Monad m, a'~m (m b)) => a' -> m b
(>>=id) :: (Monad m) => m (m b) -> m b
这样做的目的是,将嵌套的monad展平到同一个monad的单个应用程序中。非常简单,事实上这是一个“更基本”的运算:数学家们有两个态射η::a->ma
(我们知道,它是返回)和μ::m(ma)->ma
——是的,这就是你刚刚发现的。在哈斯克尔,它被称为
查看>=
的签名,\x->same x
如何使用a->mb
进行类型检查
其实很简单。查看类型签名:
same :: x -> x
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(>>= same) :: Monad m => m a -> (a -> m b) -> m b
|________|
|
x -> x
因此:
x := a
-- and
x := m b
-- and by transitivity
a := x := m b
-- or
a := m b
因此:
(>>= same) :: Monad m => m (m b) -> m b
这只是来自Control.Monad
模块的join
函数,对于list Monad,它与concat
函数相同。因此:
[[1]] >>= \x -> same x
-- is the same as the following via eta reduction
[[1]] >>= same
-- is the same as
(>>= same) [[1]]
-- is the same as
join [[1]]
-- is the same as
concat [[1]]
-- evaluates to
[1]
我本以为\x->same x
会产生[b]
类型,因为据我所知,这里的单子m
类型是[]
确实如此。具有类型x->x
的\x->same x
函数专用于类型[b]->[b]
,如上所述。因此,(>>=same)
的类型为[[b]]->[b]
,与concat
函数相同。它将列表展平
concat
函数是join
函数的一个特化,它将嵌套的单子展平
应该注意的是,monad可以用>=
或fmap
和join
来定义。致:
尽管Haskell根据return
和>=
函数定义了单子,但也可以根据return
和其他两个操作join
和fmap
定义单子。这个公式更符合范畴论中单子的原始定义。fmap
操作的类型为Monad m=>(a->b)->ma->mb
,它在两种类型之间取一个函数,并生成一个对Monad中的值执行“相同操作”的函数。使用类型为Monad m=>m(ma)->ma
的join
操作将两层一元信息“展平”为一层
这两种配方的关系如下:
fmap f m = m >>= (return . f)
join n = n >>= id
m >>= g ≡ join (fmap g m)
这里,m
具有类型Monad m=>ma
,n
具有类型Monad m=>m(ma)
,f
具有类型a->b
,g
具有类型Monad m=>a->mb,其中a
和b
是底层类型
fmap
函数是为类型和函数类别中的任何函子定义的,而不仅仅是为单子定义的。它应满足函子定律:
fmap id ≡ id
fmap (f . g) ≡ (fmap f) . (fmap g)
return
函数通过考虑将值“提升”到函子中的能力来刻画同一类别中的指向函子。它应符合以下法律:
return . f ≡ fmap f . return
此外,join
函数是单子的特征:
join . fmap join ≡ join . join
join . fmap return ≡ join . return = id
join . fmap (fmap f) ≡ fmap f . join
希望这能有所帮助。正如一些人评论的那样,你在这里发现了一个关于单子的非常可爱的特性。为了便于参考,让我们看看bind的签名:
:: Monad m => m a -> (a -> m b) -> m b
在您的例子中,类型a===mb
,就像您有a[[a]]
或m(ma)
一样。因此,如果您重写上述绑定操作的签名,您将得到:
:: Monad m => m (m b) -> ((m b) -> m b) -> m b
我提到这很可爱,因为从广义上讲,这适用于任何嵌套的monad。e、 g
:: [[b]] -> ([b] -> [b]) -> [b]
:: Maybe (Maybe b) -> (Maybe b -> Maybe b) -> Maybe b
:: Reader (Reader b) -> (Reader b -> Reader b) -> Reader b
如果您查看此处应用的get函数,您将看到它是标识函数(例如,id
<