Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm working on Problem 19 in Ninety-Nine Haskell Problems, and I've encountered the following difficulty. The problem asks to "rotate a list N places to the left." This could easily be achieved in a pointed way, e.g.,

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

However, for my own edification and for the challenge, I'd like to implement this in a point-free way using applicative functors. For instance, one can eliminate one of the arguments by using the fact that (->) [a] is an Applicative functor and implement rotate as follows:

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

Ideally, one should be able to eliminate both arguments, and write it as

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

but this causes a type error. (I'm not sure exactly how the type are being inferred, but the problem seems to be coming from the fact that the inferred Applicative functor is (->) Int rather than (->) ((->) Int [a]).)

One way to solve this would be to manually implement (->) ((->) a b) as an instance of Applicative, and, in particular, set

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

but it seems that there should be a cleaner way to do this inline. What is the "right" way to solve this problem?

like image 410
jgaeb Avatar asked Nov 29 '22 21:11

jgaeb


1 Answers

There's an "optimal" way of doing this without using the Applicative instance.

import Data.Semigroup
rotate = drop <> take

We can be explicit about the type (<>) is instantiated at

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

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

Resolved using these instances:

instance Semigroup b => Semigroup (a -> b)
instance                Semigroup [a]
like image 149
FrownyFrog Avatar answered Dec 05 '22 03:12

FrownyFrog