我正在研究Ninety-Nine Haskell Problems中的问题19,遇到了以下困难。问题要求“将列表向左旋转N个位置”。这很容易以明确的方式实现,例如
rotate :: [a] -> Int -> [a]
rotate xs n = drop n xs ++ take n xs
但是,对于我自己的教育和挑战,我想使用应用函子以一种无意义的方式实现这一点。例如,可以通过使用(->) [a]
是Applicative
函数的事实消除参数的 one ,并实现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)
,但是似乎应该有一种更简洁的方法来内联。解决此问题的“正确”方法是什么?
答案 0 :(得分:13)
有一种不使用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]
答案 1 :(得分:8)
两个选择:
rotate = liftA2 (liftA2 (++)) drop take
rotate = getCompose (liftA2 (++) (Compose drop) (Compose take))
在内联Compose
的{{1}}实例的实例方法定义之后,后者变为前者。
当然,如果愿意,您可以恢复用Applicative
和liftA2
来拼写(<$>)
。