正如其他人所建议的那样,从列表理解开始可能是明智的:
-- | Repeat each element of a list twice.
double :: [x] -> [x]
double xs = [d | x <- xs, d <- [x, x]]
但是,无论 的值如何,推导式中的第二个列表总是具有相同数量的元素,这x意味着我们不需要那么多的权力:Applicative接口就足够了。让我们从稍微不同的理解开始:
double xs = xs >>= \x -> [x, x] >>= \d -> pure d
我们可以使用 monad 恒等律立即简化:
double xs = xs >>= \x -> [x, x]
现在我们切换到Applicative,但是让我们为困难的部分留一个洞:
double :: [x] -> [x]
double xs = liftA2 _1 xs [False, True]
编译器让我们知道
_1 :: x -> Bool -> x
由于内部/第二个列表的元素始终相同,并且始终来自当前外部/第一个列表元素,因此我们不必关心Bool:
double xs = liftA2 const xs [False, True]
事实上,我们甚至不需要能够区分列表位置:
double xs = liftA2 const xs [(),()]
当然,我们有一个特殊的Applicative方法,(<*),它对应于liftA2 const,所以让我们使用它:
double xs = xs <* [(),()]
然后,如果我们愿意,我们可以xs通过切换到“无点”形式来避免提及:
-- | Repeat each element of a list twice.
double :: [x] -> [x]
double = (<* [(),()])
现在进行测试:
main :: IO ()
main = print $ double [1..3]
这将打印[1,1,2,2,3,3].
double承认可疑价值的轻微概括:
double :: Alternative f => f x -> f x
double = (<* join (<|>) (pure ()))
这将适用于序列和列表:
double (Data.Sequence.fromList [1..3]) = Data.Sequence.fromList [1,1,2,2,3,3]
但对于其他一些情况可能会有点混乱Alternative:
double (Just 3) = Just 3