Vyacheslav Siniy
Vyacheslav Siniy

Reputation: 35

Haskell Lens. Get an element before the last

Is there a way to get the element before the last using hakell lens? For example, I have such a structure:

pp = (1,2,3,4)

I want to do something like pp ^. _almostLast and get 3. I can't use _3 because (suppose) I don't know how much elements there are in this tuple.

Upvotes: 1

Views: 94

Answers (1)

Hjulle
Hjulle

Reputation: 2615

If you want your code to be flexible over the number of elements in a tuple, one option is to create your own type class, similar to the Field2 class in Control.Lens.Tuple, which provides the _2 function.

This class and the instances for the first few tuples look like this (after removing some comments and irrelevant code):

-- | Provides access to the 2nd field of a tuple.
class Field2 s t a b | s -> a, t -> b, s b -> t, t a -> s where
  _2 :: Lens s t a b

instance Field2 (a,b) (a,b') b b' where
  _2 k ~(a,b) = k b <&> \b' -> (a,b')
  {-# INLINE _2 #-}

instance Field2 (a,b,c) (a,b',c) b b' where
  _2 k ~(a,b,c) = k b <&> \b' -> (a,b',c)
  {-# INLINE _2 #-}

instance Field2 (a,b,c,d) (a,b',c,d) b b' where
  _2 k ~(a,b,c,d) = k b <&> \b' -> (a,b',c,d)
  {-# INLINE _2 #-}

If we now want to make a variant which instead picks the second-to-last element, we just have to tweak the code to do that:

 -- | Provides access to the 2nd to last field of a tuple.
class AlmostLast s t a b | s -> a, t -> b, s b -> t, t a -> s where
  _almostLast :: Lens s t a b

instance AlmostLast (a,b) (a',b) a a' where
  _almostLast k ~(a,b) = k a <&> \a' -> (a',b)
  {-# INLINE _almostLast #-}

instance AlmostLast (a,b,c) (a,b',c) b b' where
  _almostLast k ~(a,b,c) = k b <&> \b' -> (a,b',c)
  {-# INLINE _almostLast #-}

instance AlmostLast (a,b,c,d) (a,b,c',d) c c' where
  _almostLast k ~(a,b,c,d) = k c <&> \c' -> (a,b,c',d)
  {-# INLINE _almostLast #-}

etc. for as many tuples you need.


But the big question here is: Why don't you know how long your tuple is? In most such cases, you would be much better off using a list, especially so you don't have to manually write a bunch of instances for each possible length of tuples you expect to encounter.

Upvotes: 1

Related Questions