0

Considering the following :

{-# LANGUAGE TemplateHaskell   #-}

import Control.Lens

data Typex = Typex 
    { _level       :: Int
    , _coordinate  :: (Int, Int)
    , _connections :: [(Int,(Int,Int))]
    } deriving Show
makeLenses ''Typex

initTypexLevel :: Int -> Int -> Int -> [Typex] 
initTypexLevel a b c = [ Typex a (x, y) [(0,(0,0))]
                       | x <- [0..b], y <- [0..c]
                       ]

buildNestedTypexs :: [(Int, Int)] -> [[Typex]]
buildNestedTypexs pts
     = setConnections [ initTypexLevel i y y
                      | (i,(_,y)) <- zip [0..] pts
                      ]

setConnections :: [[Typex]] -> [[Typex]]
setConnections = ?

How can I uses lenses to modify the connections in allTypexs with a function of type [[Typex]] -> [[Typex]] in such a way that in each Typex

connections = [(level of Typex being modified +1, (x, y))] where
x,y = 0..(length of next [Typex] in [[Typex]])/2

X and y both need to go through that length of the next [Typex]. The final [Typex] should be left unchanged if possible. So all the connections of each Typex in the same [Typex] are the same.

Output for setConnections $ buildNestedTypexs [(0,1),(1,1)] should be:

[ [ Typex { _level = 0
          , _coordinate = (0,0)
          , _connections = [(1,(0,0)), (1,(0,1)), (1,(1,0)), (1,(1,1))] }
  , Typex { _level = 0
          , _coordinate = (0,1)
          , _connections = [(1,(0,0)), (1,(0,1)), (1,(1,0)), (1,(1,1))] }
  , Typex { _level = 0
          , _coordinate = (1,0)
          , _connections = [(1,(0,0)), (1,(0,1)), (1,(1,0)), (1,(1,1))] }
  , Typex { _level = 0
          , _coordinate = (1,1)
          , _connections = [(1,(0,0)), (1,(0,1)), (1,(1,0)), (1,(1,1))] }
  ]
 ,[ Typex { _level = 1
          , _coordinate = (0,0)
          , _connections = [(0,(0,0))] }
  , Typex { _level = 1
          , _coordinate = (0,1)
          , _connections = [(0,(0,0))] }
  , Typex { _level = 1
          , _coordinate = (1,0)
          , _connections = [(0,(0,0))] }
  , Typex { _level = 1
          , _coordinate = (1,1)
          , _connections = [(0,(0,0))] }
  ]]

I suppose I'll need to import Control.Lens.Indexed but that's about it so all help is appreciated.

LHurttila
  • 37
  • 6
  • 2
    A concrete example would be useful to see what exactly the question is. – leftaroundabout Mar 05 '20 at 12:28
  • @leftaroundabout I added more explanation and an expected result. In it the connections of `Typex`s with level 1 were left unchanged, because they're part of the last [Typex] given as an argument. – LHurttila Mar 05 '20 at 13:44
  • Ok, I guess it's technically clear now, but... can't you simplify/generalise the question a bit? This still seems quite involved with domain-specific details that aren't really relevant to the core of what you're asking. – leftaroundabout Mar 05 '20 at 13:58
  • @leftaroundabout Thanks for making that a bit more readable. I removed the Double part from connections because it was irrelevant considering the problem. Other than that I don't really know how to make this more general without adding a bunch of variables which would probably make it even more difficult to understand. – LHurttila Mar 05 '20 at 14:17
  • Can we also get rid of the `c` parameter and avoid nested tuples altogether? – leftaroundabout Mar 05 '20 at 14:48
  • @leftaroundabout Not really because I'll need that nested tuple in the final solution anyway. Nevertheless, a working solution even without it is still useful. – LHurttila Mar 05 '20 at 15:10
  • I'm confused about the description of the desired transformation versus the example given. Since the second `[Typex]` has length 4, shouldn't the connections in the first `[Typex]` be set to `[(1,(x,y)) | x <- [0..4], y <- [0..4]]`? Or is "length" supposed to be "index"? – K. A. Buhr Mar 05 '20 at 18:56
  • @K.A.Buhr True, there was a mistake in the explanation of desired transformation but it should be right now. The idea is that every `Typex` should be connected to every `Typex` in the next `[Typex]` of `[[Typex]]` – LHurttila Mar 05 '20 at 19:48

1 Answers1

3

Is this what you want?

{-# LANGUAGE TupleSections #-}

setConnections :: [[Typex]] -> [[Typex]]
setConnections (x:rest@(y:_)) = map (connect y) x : setConnections rest
  where connect :: [Typex] -> Typex -> Typex
        connect txs tx
          = tx & connections .~ (map ((tx ^. level) + 1,) $ txs ^.. traverse.coordinate)
setConnections lst = lst

This isn't a pure lens solution, but I find that as a general rule when working with lenses, it's not always a good idea to get the lenses to do everything. It just makes things difficult to write and hard to understand.

Here, I've used "plain Haskell" in a lot of places: to pattern matching with a manual recursion to process pairs x,y of consecutive [Typex]s and I've used map to connect each Typex in the first x :: [Typex] with the second y :: [Typex]. I've also used map to add the new level to the coordinate list to generate the new connections value.

The only lens expressions used here are:

  • tx & connections .~ (...) which replaces the connections field of tx :: Typex with a new value
  • tx ^. level which fetches the level of the current tx :: Typex
  • txs ^.. traverse.coordinate which fetches the coordinate fields of all Typex values in the list txs :: [Typex] and returns them as a list [(Int,Int)]

In my opinion, this sort of balance between lenses and "plain Haskell" is the best way of dealing with complex transformations.

K. A. Buhr
  • 45,621
  • 3
  • 45
  • 71
  • I tried that solution with different input values and it seems to work exactly as it should. Thank you. Now I just have to figure out what it actually does in each part to modify it a bit :) – LHurttila Mar 06 '20 at 07:15
  • 2
    I added some explanation of which lens expressions do what, in case that helps. – K. A. Buhr Mar 06 '20 at 15:51