将`(->) ((->) 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))
后者在 Compose
的 Applicative
实例的实例方法定义内联后成为前者。
当然,如果您愿意,您可以恢复使用 (<$>)
和 (<*>)
拼写 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]
我正在 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))
后者在 Compose
的 Applicative
实例的实例方法定义内联后成为前者。
当然,如果您愿意,您可以恢复使用 (<$>)
和 (<*>)
拼写 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]