将`(->) ((->) a b)` 实现为应用仿函数的最佳方法是什么?

What is the best way to realize `(->) ((->) a b)` as an applicative functor?

我正在 Ninety-Nine Haskell Problems 中解决问题 19,我遇到了以下困难。问题要求 "rotate a list N places to the left." 这可以很容易地以有针对性的方式实现,例如,

rotate :: [a] -> Int -> [a]
rotate xs n = drop n xs ++ take n xs

但是,为了我自己的启发和挑战,我想使用应用函子以无点的方式实现它。例如,可以通过使用 (->) [a] 是一个 Applicative 函子这一事实来消除 一个 参数,并按如下方式实现 rotate

rotate :: Int -> [a] -> [a]
rotate n = (++) <$> drop n <*> take n

理想情况下,应该能够消除两个参数,并将其写为

rotate :: [a] -> Int -> [a]
rotate :: (++) <$> drop <*> take

但这会导致类型错误。 (我不确定类型是如何被推断出来的,但问题似乎来自于推断的 Applicative 仿函数是 (->) Int 而不是 (->) ((->) Int [a])。)

解决这个问题的一种方法是手动实现 (->) ((->) a b) 作为 Applicative 的实例,特别是设置

<*> f g x y = f x y (g x y)

但似乎应该有一种更简洁的方式来执行此内联操作。 "right" 解决这个问题的方法是什么?

两个选择:

rotate = liftA2 (liftA2 (++)) drop take
rotate = getCompose (liftA2 (++) (Compose drop) (Compose take))

后者在 ComposeApplicative 实例的实例方法定义内联后成为前者。

当然,如果您愿意,您可以恢复使用 (<$>)(<*>) 拼写 liftA2

有一种 "optimal" 方法可以在不使用 Applicative 实例的情况下执行此操作。

import Data.Semigroup
rotate = drop <> take

我们可以明确(<>)

实例化的类型
{-# Language ScopedTypeVariables #-}
{-# Language TypeApplications    #-}

rotate :: forall a. Int -> [a] -> [a]
rotate = (<>) @(Int -> [a] -> [a]) drop take

使用这些实例解决:

instance Semigroup b => Semigroup (a -> b)
instance                Semigroup [a]