I'd like to perform a sort based on a specific value within a record. As such I was thinking of passing a lens into a lensSort
function but I've been unable to make it work.
Ideally I could do something like this
lensSort :: HasLens a => Lens' a b -> a -> a -> -> Ordering
lensSort lens x y | x ^. lens > y ^. lens = GT
| x ^. lens < y ^. lens = LT
| otherwise = GT
And be able to call it with something like
data Rectangle = Rectangle { _height :: Int, _width :: Int }
makeLenses'' Rectangle
let foo = [Rectangle 1 2, Rectangle 2 1]
sortBy (lensSort height) foo
I'm failing to get this to work, and concerned I might be barking up the wrong tree completely, I'm still new to Haskell.
Aside from typos, your code actually works pretty much as it is – the only things that obviously needed is that b
be in fact comparable. The following works:
{-# LANGUAGE TemplateHaskell, RankNTypes #-}
import Control.Lens
import Control.Lens.TH
import Data.List
data Rectangle = Rectangle { _height :: Int, _width :: Int }
deriving (Show)
makeLenses ''Rectangle
lensSort :: Ord b => Lens' a b -> a -> a -> Ordering
lensSort lens x y | x ^. lens > y ^. lens = GT
| x ^. lens < y ^. lens = LT
| otherwise = GT
foo :: [Rectangle]
foo = [Rectangle 1 2, Rectangle 2 1]
main = print $ sortBy (lensSort height) foo
-- [Rectangle {_height = 1, _width = 2},Rectangle {_height = 2, _width = 1}]
Note that there is not really a need to pass around an actual lens, because you only use it as a getter (≅function) anyway. So you can just do
import Data.Ord (comparing)
main = print $ sortBy (comparing (^.height))
...without any extra definitions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With