我不能理解fp-course中的一个练习题的答案。
https://github.com/system-f/fp-course/blob/7a957a177ccdc9d91323c32d576351dcc1234f2f/src/Course/Monad.hs#L119
-- | Flattens a combined structure to a single structure.
--
-- >>> join ((1 :. 2 :. 3 :. Nil) :. (1 :. 2 :. Nil) :. Nil)
-- [1,2,3,1,2]
--
-- >>> join (Full Empty)
-- Empty
--
-- >>> join (Full (Full 7))
-- Full 7
--
-- >>> join (+) 7
-- 14
join ::
Monad k =>
k (k a)
-> k a
join x = id =<< x
而id :: a -> a
和(=<<) :: (a -> k b) -> k a -> k b
为什么id
可以是(=<<)
的第一个参数?
为什么答案是join x = id =<< x
?
1条答案
按热度按时间xqk2d5yq1#
为什么
id
可以是(=<<)
的第一个参数?也许在类型必须相等的地方将类型一对一地排列会有帮助。这对应于编译器在类型检查期间所做的 * 统一 。
| ↓术语|类型→|||||||||||
| --------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|--------------|
|
(=<<)
|- -|(|a1|→|k**B|)|→|k|a1|→|k**B||
id
|- -||a2|→|a2||||||||
(=<<) id
|- -|||||||k|a1|→|k**B|| | | | ↑|||||k|a2|→|k**B|
| | | | | | ↑|||k|(*kB)|→|*kB*|
注意,有两个不同类型的变量名为
a
。在GHCi中,您可以设置:set -fprint-explicit-foralls
,然后:type id
将显示forall a. a -> a
。这显示了参数a
的局部作用域,就像\x -> …
定义x
的作用域一样。为什么答案是
join x = id =<< x
?(=<<)
是mapping(fmap
/(<$>)
)和flattening(join
)的组合,即f =<< x = join (fmap f x)
。由于join
仅为flattening,因此我们可以通过将“mapping”部分设置为id
,使用(=<<)
表示join
。为什么
id =<< (Full 8)
不能变成8
id =<< Full (Full 8)
意味着我们将id
Map到所有元素fmap id (Full (Full 8))
=Full (id (Full 8))
=Full (Full 8)
上,然后将Optional (Optional t1)
平坦化为Optional t1
,对于某种类型t1
,其中8 :: (Num t1) => t1
。如果我们尝试
id =<< Full 8
,那么我们可以Mapfmap id (Full 8)
=Full (id 8)
=Full 8
,但我们只有Optional t1
。这不是错误,但它增加了一个约束:对于某种类型t2
,t1
必须等于Optional t2
。该等式约束的语法是t1 ~ Optional t2
。但是没有任何类型可以工作,除非您为Num (Optional t2)
添加一个示例。(这在技术上是可行的,但不是标准的。)