我正在编写以下代码片段:
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代替for。在进行上述更改之前,我首先切换到了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的操作中使用do表示法/返回。
在这里,我显然缺少了一些基本的东西,无论是关于for签名真正意味着什么,还是我发布的代码是如何被编译器解释的,并且希望能帮助理解所发生的事情。
发布于 2016-12-14 19:30:32
第一个简短的答案是Parser有一个Applicative实例。小片段
do
a' <- parseJSON a
b' <- parseJSON b
return (a', b')具有类型Parser (Whitelisted, WhitelistComment),它在类型签名中与f b统一。
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)因为有一个Applicative Parser实例,所以它也满足了这个约束。(我认为a'和b'的类型是正确的)
第二个简短的答案是,Monad比Applicative更强大,只要您需要Applicative,就可以使用Monad。自从提案实现以来,每个Monad都是Applicative。Monad类现在看起来像
class Applicative m => Monad m where
...Monad比Applicative功能更强大,在任何需要Applicative的地方,您都可以使用Monad替换如下:
<*>return而不是pureliftM而不是fmap如果您正在编写某种新类型SomeMonad,并且为Monad类提供了一个实例,您也可以使用它为Applicative和Functor提供实例。
import Control.Monad
instance Applicative SomeMonad where
pure = return
(<*>) = ap
instance Functor SomeMonad where
fmap = liftM发布于 2016-12-14 23:23:35
这只是通常的调用者-vs-实现者二元性,其中一方获得灵活性,而另一方则受到限制。
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)显然,一旦您这样做了,您就没有理由不能在传递给Parser的函数中使用任何-specific功能,包括Monad之类的功能。
另一方面,for的实现者受到for接口多态性的限制。他们必须使用--任何选择的f,所以他们只能在为实现for而编写的代码中使用Applicative接口。但这只会限制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的实现者将不得不选择用为f选择的for调用者的相同类型实例化g,以便得到最终的f (t b)返回值。但是for的调用者仍然受到类型系统的限制,只能提供一个适用于任何Applicative g的函数。
关键是,如果您选择使用哪种类型实例化多态签名,那么您就不会受到该接口的限制。您可以选择一个类型,然后使用该类型的任何其他功能,只要您仍然提供接口所需的信息。也就是说,您可以使用非Traversable功能来创建您的t a,使用非Applicative功能来创建您的a -> f b,所需要的只是提供这些输入。实际上,您几乎必须使用特定于a和b的功能。多态签名的实现者并没有得到这种自由,他们受到多态性的限制,只做那些为任何可能的选择工作的事情。
顺便说一句,类似于等级2类型如何在角色反转的情况下添加这种二元性的“另一个层次”(而秩N类型允许任意多个级别),在约束本身中也可以看到类似的二元性(再次翻转)。再考虑一下签名:
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)for的调用方在选择t和f类型时受到Traversable和Applicative约束的限制。实现者可以自由地使用这些约束所隐含的任何函数,而不必担心如何证明这些约束是满足的。
https://stackoverflow.com/questions/41150270
复制相似问题