实现`(->)((->)ab)`为应用函子的最佳方法是什么?

jga*_*aeb 4 haskell applicative

我正在研究“ 九十九个Haskell问题”中的问题19 ,并且遇到了以下困难。问题要求“将列表向左旋转N个位置”。这很容易以明确的方式实现,例如

rotate :: [a] -> Int -> [a]
rotate xs n = drop n xs ++ take n xs
Run Code Online (Sandbox Code Playgroud)

但是,对于我自己的教育和挑战,我想使用应用函子以一种无意义的方式实现这一点。举例来说,一个可以消除一个通过事实的论据(->) [a]Applicative仿函数并实现rotate如下:

rotate :: Int -> [a] -> [a]
rotate n = (++) <$> drop n <*> take n
Run Code Online (Sandbox Code Playgroud)

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

rotate :: [a] -> Int -> [a]
rotate :: (++) <$> drop <*> take
Run Code Online (Sandbox Code Playgroud)

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

解决此问题的一种方法是将手动实现(->) ((->) a b)Applicative,尤其是将set设置为

<*> f g x y = f x y (g x y)
Run Code Online (Sandbox Code Playgroud)

但似乎应该有一种更简洁的方法来内联。解决此问题的“正确”方法是什么?

Fro*_*rog 13

有一种不使用Applicative实例的“最佳”方法。

import Data.Semigroup
rotate = drop <> take
Run Code Online (Sandbox Code Playgroud)

我们可以明确地知道(<>)实例化的类型

{-# Language ScopedTypeVariables #-}
{-# Language TypeApplications    #-}

rotate :: forall a. Int -> [a] -> [a]
rotate = (<>) @(Int -> [a] -> [a]) drop take
Run Code Online (Sandbox Code Playgroud)

使用以下实例解决:

instance Semigroup b => Semigroup (a -> b)
instance                Semigroup [a]
Run Code Online (Sandbox Code Playgroud)


Dan*_*ner 8

两种选择:

rotate = liftA2 (liftA2 (++)) drop take
rotate = getCompose (liftA2 (++) (Compose drop) (Compose take))
Run Code Online (Sandbox Code Playgroud)

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

您可以恢复使用拼写liftA2s的功能,当然(<$>)(<*>)如果愿意,可以使用。

  • 首先,我已经习惯了短语((liftA2。liftA2)(++)drop drop)。它暗示(正确地)您可以根据需要放置尽可能多的`liftA2`以得到合适的组合器。 (3认同)