我刚刚看到一个表情
fmap_List :: (a -> b) -> [] a -> [] b
-- "[] a" means "[a]", for types.
fmap_List f [] = []
fmap_List f (x:xs) = f x : fmap_List f xs
由于[] a
的意思是[a]
,为什么我们不直接放置[a]
?有一些特殊情况我们应该使用[] a
吗?
答案 0 :(得分:11)
这只是避免使用语法糖[a]
,以说明f
的{{1}}类型参数已被类型构造函数fmap :: Functor f => (a -> b) -> f a -> f b
替代,其他类型的构造函数。
也就是说,当您想强调与某个类型构造函数(例如[]
)上具有多态性的签名之间的关系时,可以将类型[a]
写为[] a
:
fmap
或fmap :: Functor f => (a -> b) -> f a -> f b
-- f = []
fmap_List :: (a -> b) -> [] a -> [] b
fmap_List = fmap
-- f = Maybe
fmap_Maybe :: (a -> b) -> Maybe a -> Maybe b
fmap_Maybe = fmap
:
join
这与join :: Monad m => m ( m a) -> m a
join_List :: [] ([] a) -> [] a
完全相同,只是更清楚地表明[[a]] -> [a]
= m
。
[]
是应用于类型变量[] a
的类型构造函数[]
。 a
或[] a
的类型为[a]
,是值所占据的类型,例如*
,Int
,Char
,或Maybe Int
。 Either String Int
具有类型[]
,该类型带有类型参数并产生结果的类型,例如* -> *
,[]
,Maybe
,或Identity
。
例如,您可以在Either e
的定义中看到这一点-我们将构造函数 instance Monad []
(类型为[]
)作为* -> *
(类型Monad
),而不是用诸如(* -> *) -> Constraint
之类的构造函数生成的类型(类型*
)。这就像使用instance Monad ([] a)
而不是Maybe
,Maybe a
,Maybe Int
,&c。
使用Maybe String
编译指示,您可以显式地将多态函数应用于类型自变量,例如在GHCi中:
TypeApplications
这对于通过使用特定于(更多)具体实例的类型来弄清楚如何使用多态函数或记录使用该函数的容器或monad很有用:
> :set -XTypeApplications
> :type fmap @[]
fmap @[] :: (a -> b) -> [a] -> [b]
> :type fmap @Maybe
fmap @Maybe :: (a -> b) -> Maybe a -> Maybe b
> :type fmap @[] @Int @Char
fmap @[] @Int @Char :: (Int -> Char) -> [Int] -> [Char]
> :type fmap @[] @_ @Bool
fmap @[] @_ @Bool :: (a -> Bool) -> [a] -> [Bool]
您还可以要求GHCi提供一种类型,以更好地了解各种类型:
> :type traverse
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
> :type traverse @[] @IO
traverse @[] @IO :: (a -> IO b) -> [a] -> IO [b]
答案 1 :(得分:4)
[]
符号在未应用但传递给另一个类型构造函数时很有用。
Fix [] -- recursion-schemes
Free [] a -- free
ReaderT r [] a -- transformers
Compose [] Maybe a -- base (Data.Functor.Compose)
答案 2 :(得分:2)
由于
[] a
的意思是[a]
,为什么我们不直接放置[a]
?
这是一个有趣的问题。让我尝试说服您,实际上,真正的问题是:
由于
[a]
的意思是[] a
,为什么我们不直接放置[] a
?
请考虑以下参数类型:
data Identity a = Identity a
data Maybe a = Nothing | Just a
data HPair a = HPair a a
data HTriple a = HTriple a a a
data Tree a = Empty | Branch (Tree a) a (Tree a)
data List a = Nil | Cons a (List a)
data [a] = [] | (a : [a])
请注意,其中所有 的格式均为TypeConstructor a
,[a]
除外!确实,为了使世界更加连贯,我们实际上应该写[] a
而不是[a]
。
这也清楚表明[]
是类型构造函数,与Identity, Maybe, Tree, ...
一样。
[a]
语法很方便,并且可能更易于阅读,特别是对于初学者。但是,这会使了解[]
的真实性质变得更加困难。相比之下,Maybe
没有这样的问题。