为什么 Data.Traversable 中的 'for' 接受单子动作?
Why does 'for' from Data.Traversable accept monadic actions?
我正在处理以下一小段代码:
import Control.Monad
import Data.Aeson
import qualified Data.HashMap.Strict as HashMap
import Data.Map (Map)
import qualified Data.Map as Map
import GHC.Generics
-- definitions of Whitelisted, WhitelistComment and their FromJSON instances
-- omitted for brevity
data Whitelist = Whitelist
{ whitelist :: Map Whitelisted WhitelistComment
} deriving (Eq, Ord, Show)
instance FromJSON Whitelist where
parseJSON (Object v) =
fmap (Whitelist . Map.fromList) . forM (HashMap.toList v) $ \(a, b) -> do
a' <- parseJSON (String a)
b' <- parseJSON b
return (a', b')
parseJSON _ = mzero
当我意识到我可以用应用风格重写 do
块时:
instance FromJSON Whitelist where
parseJSON (Object v) =
fmap (Whitelist . Map.fromList) . forM (HashMap.toList v) $ \(a, b) ->
(,) <$> parseJSON (String a) <*> parseJSON b
parseJSON _ = mzero
我还可以用 for
替换 forM
。在进行上述更改之前,我先切换到 for
:
instance FromJSON Whitelist where
parseJSON (Object v) =
fmap (Whitelist . Map.fromList) . for (HashMap.toList v) $ \(a, b) -> do
a' <- parseJSON (String a)
b' <- parseJSON b
return (a', b')
parseJSON _ = mzero
令我惊讶的是这仍然编译。给定 for
的定义:
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
我认为 Applicative
约束会阻止我在传递给 for
的操作中使用符号 / return。
我在这里显然遗漏了一些基本的东西,无论是 for
签名的真正含义,还是编译器如何解释我发布的代码,如果能帮助我理解正在发生的事情,我将不胜感激。
第一个简短的回答是 Parser
有一个 Applicative
实例。片段
do
a' <- parseJSON a
b' <- parseJSON b
return (a', b')
在类型签名
中具有与f b
统一的类型Parser (Whitelisted, WhitelistComment)
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
因为有一个 Applicative Parser
实例,它也满足该约束。 (我想我得到了 a'
和 b'
的正确类型)
第二个简短的回答是 Monad
严格来说比 Applicative
更强大,任何需要 Applicative
的地方都可以使用 Monad
。于是乎 Monad
-Applicative
proposal was implemented, every Monad
is also Applicative
. The Monad
class 现在变成了
class Applicative m => Monad m where
...
A Monad
严格来说比 Applicative
更强大,任何你需要 Applicative
的地方你都可以使用 Monad
代替以下替换:
如果您正在编写一些新类型 SomeMonad
,并且已经为 Monad
class 提供了一个实例,您可以使用它来为 Applicative
提供实例] 和 Functor
也是。
import Control.Monad
instance Applicative SomeMonad where
pure = return
(<*>) = ap
instance Functor SomeMonad where
fmap = liftM
这只是通常的调用者与实施者的二元对立,一方获得灵活性,另一方获得限制。
for
为您提供了这个界面:
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
作为调用者,您可以灵活地选择任何类型 f
来实例化它,因此您可以像使用它一样使用它:
for :: Traversable t => t a -> (a -> Parser b) -> Parser (t b)
很明显,一旦您完成了该操作,就没有理由不能在传递给 for
的函数中使用任何 Parser
特定的功能,包括 Monad
内容。
另一方面,for
的实现者受到for
接口中多态性的限制。他们必须使用 any 选择 f
,因此他们只能 在代码中使用 Applicative
接口写实现 for
。但这只限制了 for
本身的代码,而不是传递给它的函数。
如果 for
的作者想限制调用者在那个函数中可以做什么,他们可以使用 RankNTypes
来提供这个接口:
for :: forall t f. (Traversable t, Applicative f) => t a -> (forall g. Applicative g => a -> g b) -> f (t b)
现在提供的 lambda 本身在 g
中必须是多态的(受 Applicative
约束)。 for
的调用者仍然可以灵活地选择 f
,而实施者只能使用 Applicative
特性。但是 for
的调用者是函数参数的 实现者 ,所以既然该函数本身是多态的,for
的调用者就只能使用 Applicative
的特性,for
的实现者可以自由地将它与他们喜欢的任何类型一起使用(包括可能使用 monad 特性将其与其他内部值组合)。使用此特定类型签名,for
的实现者将不得不选择实例化 g
与 for
的调用者为 f
选择的相同类型,以便提出最终的 f (t b)
return 值。但是 for
的调用者仍然会受到类型系统的限制,只能提供适用于任何 Applicative g
.
的函数
关键是,如果您可以选择用什么类型来实例化多态签名,那么您就不会受到该接口的限制。您可以选择一种类型,然后使用您喜欢的该类型的任何其他功能,前提是您仍然提供界面需要您提供的信息位。即,您可以使用非 Traversable
功能来创建 t a
,使用非 Applicative
功能来创建 a -> f b
,所需要的只是提供这些输入。事实上,您几乎 可以使用a
和b
的特定功能。多态签名的 实现者 没有那种自由,他们受多态性的限制,只能做对任何可能的选择都有效的事情。
顺便说一句,类似于等级 2 类型如何添加 "another level" 这种二元性,角色颠倒(并且等级 N 类型允许任意多个级别),也可以看到类似的二元性(再次翻转)在约束本身。再考虑签名:
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
for
的调用者在选择类型t
和[时受到Traversable
和Applicative
约束的限制=15=]。实施者可以自由使用这些约束所隐含的任何功能,而不必担心如何证明约束得到满足。
我正在处理以下一小段代码:
import Control.Monad
import Data.Aeson
import qualified Data.HashMap.Strict as HashMap
import Data.Map (Map)
import qualified Data.Map as Map
import GHC.Generics
-- definitions of Whitelisted, WhitelistComment and their FromJSON instances
-- omitted for brevity
data Whitelist = Whitelist
{ whitelist :: Map Whitelisted WhitelistComment
} deriving (Eq, Ord, Show)
instance FromJSON Whitelist where
parseJSON (Object v) =
fmap (Whitelist . Map.fromList) . forM (HashMap.toList v) $ \(a, b) -> do
a' <- parseJSON (String a)
b' <- parseJSON b
return (a', b')
parseJSON _ = mzero
当我意识到我可以用应用风格重写 do
块时:
instance FromJSON Whitelist where
parseJSON (Object v) =
fmap (Whitelist . Map.fromList) . forM (HashMap.toList v) $ \(a, b) ->
(,) <$> parseJSON (String a) <*> parseJSON b
parseJSON _ = mzero
我还可以用 for
替换 forM
。在进行上述更改之前,我先切换到 for
:
instance FromJSON Whitelist where
parseJSON (Object v) =
fmap (Whitelist . Map.fromList) . for (HashMap.toList v) $ \(a, b) -> do
a' <- parseJSON (String a)
b' <- parseJSON b
return (a', b')
parseJSON _ = mzero
令我惊讶的是这仍然编译。给定 for
的定义:
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
我认为 Applicative
约束会阻止我在传递给 for
的操作中使用符号 / return。
我在这里显然遗漏了一些基本的东西,无论是 for
签名的真正含义,还是编译器如何解释我发布的代码,如果能帮助我理解正在发生的事情,我将不胜感激。
第一个简短的回答是 Parser
有一个 Applicative
实例。片段
do
a' <- parseJSON a
b' <- parseJSON b
return (a', b')
在类型签名
中具有与f b
统一的类型Parser (Whitelisted, WhitelistComment)
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
因为有一个 Applicative Parser
实例,它也满足该约束。 (我想我得到了 a'
和 b'
的正确类型)
第二个简短的回答是 Monad
严格来说比 Applicative
更强大,任何需要 Applicative
的地方都可以使用 Monad
。于是乎 Monad
-Applicative
proposal was implemented, every Monad
is also Applicative
. The Monad
class 现在变成了
class Applicative m => Monad m where
...
A Monad
严格来说比 Applicative
更强大,任何你需要 Applicative
的地方你都可以使用 Monad
代替以下替换:
如果您正在编写一些新类型 SomeMonad
,并且已经为 Monad
class 提供了一个实例,您可以使用它来为 Applicative
提供实例] 和 Functor
也是。
import Control.Monad
instance Applicative SomeMonad where
pure = return
(<*>) = ap
instance Functor SomeMonad where
fmap = liftM
这只是通常的调用者与实施者的二元对立,一方获得灵活性,另一方获得限制。
for
为您提供了这个界面:
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
作为调用者,您可以灵活地选择任何类型 f
来实例化它,因此您可以像使用它一样使用它:
for :: Traversable t => t a -> (a -> Parser b) -> Parser (t b)
很明显,一旦您完成了该操作,就没有理由不能在传递给 for
的函数中使用任何 Parser
特定的功能,包括 Monad
内容。
另一方面,for
的实现者受到for
接口中多态性的限制。他们必须使用 any 选择 f
,因此他们只能 在代码中使用 Applicative
接口写实现 for
。但这只限制了 for
本身的代码,而不是传递给它的函数。
如果 for
的作者想限制调用者在那个函数中可以做什么,他们可以使用 RankNTypes
来提供这个接口:
for :: forall t f. (Traversable t, Applicative f) => t a -> (forall g. Applicative g => a -> g b) -> f (t b)
现在提供的 lambda 本身在 g
中必须是多态的(受 Applicative
约束)。 for
的调用者仍然可以灵活地选择 f
,而实施者只能使用 Applicative
特性。但是 for
的调用者是函数参数的 实现者 ,所以既然该函数本身是多态的,for
的调用者就只能使用 Applicative
的特性,for
的实现者可以自由地将它与他们喜欢的任何类型一起使用(包括可能使用 monad 特性将其与其他内部值组合)。使用此特定类型签名,for
的实现者将不得不选择实例化 g
与 for
的调用者为 f
选择的相同类型,以便提出最终的 f (t b)
return 值。但是 for
的调用者仍然会受到类型系统的限制,只能提供适用于任何 Applicative g
.
关键是,如果您可以选择用什么类型来实例化多态签名,那么您就不会受到该接口的限制。您可以选择一种类型,然后使用您喜欢的该类型的任何其他功能,前提是您仍然提供界面需要您提供的信息位。即,您可以使用非 Traversable
功能来创建 t a
,使用非 Applicative
功能来创建 a -> f b
,所需要的只是提供这些输入。事实上,您几乎 可以使用a
和b
的特定功能。多态签名的 实现者 没有那种自由,他们受多态性的限制,只能做对任何可能的选择都有效的事情。
顺便说一句,类似于等级 2 类型如何添加 "another level" 这种二元性,角色颠倒(并且等级 N 类型允许任意多个级别),也可以看到类似的二元性(再次翻转)在约束本身。再考虑签名:
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
for
的调用者在选择类型t
和[时受到Traversable
和Applicative
约束的限制=15=]。实施者可以自由使用这些约束所隐含的任何功能,而不必担心如何证明约束得到满足。