Как использовать iset с вложенным типом данных и линзами?

Я не могу выровнять типы в последней функции. Суть в том, чтобы установить все ценовые удвоения в соединениях с функцией, которая зависит только от индекса тройки. Исходное значение Double в кортеже можно отбросить.

{-# LANGUAGE TemplateHaskell   #-}
{-# LANGUAGE TupleSections #-}

import Control.Lens

data Typex = Typex 
    { _level       :: Int
    , _coordinate  :: (Int, Int)
    , _connections :: [(Int, (Int, Int), Double)]  -- = (level, coordinate, price)
    } deriving Show
makeLenses ''Typex

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

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

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

setInitPrices  :: [[Typex]] -> [[Typex]]
setInitPrices  (x:rest) = map setIndexPrices x : setInitPrices  rest
  where setIndexPrices :: Typex -> Typex
        setIndexPrices tx =  n & connections .~ ??? -- using iset (?), set the price in every 3-tuple so that price = f (index of the 3-tuple) where f = i*2
setInitPrices  lst = lst

person LHurttila    schedule 09.03.2020    source источник


Ответы (1)


Вероятно, вы ищете:

  where setIndexPrices :: Typex -> Typex
        setIndexPrices tx =  tx & connections .> traversed <. _3 .@~ f
        f i = 2 * fromIntegral i

Здесь .@~ — это версия оператора iset, а .> и <. — варианты оператора композиции ., используемого для объединения индексированной оптики.

Если вы рассматриваете более простую неиндексированную оптику:

connections . traverse . _3

Эта оптика берет TypeX, фокусируется на его _connections полях, просматривает список подключений и фокусируется на третьем поле (цене) каждого подключения. Результатом является оптика, которая пересекает все цены в TypeX по порядку.

Чтобы проиндексировать эту оптику, нам нужно «обновить» неиндексированный traverse до индексированного traversed. Затем мы хотим использовать сохраняющие индекс операторы композиции .> и <., где знаки меньше/больше указывают на часть оптики, которая имеет нужный нам индекс. (В более сложных сценариях с несколькими индексами можно использовать <.> для объединения индексов из двух оптических систем в пары индексов (i,j).)

Вот так мы получаем:

connections .> traversed <. _3

Он по-прежнему проходит все цены в TypeX по порядку, но также переносит индекс из обхода.

Обратите внимание, что setInitPrices на самом деле является одной из тех функций, которые легко написать как вычисление линзы «все сразу». map setIndexPrices и рекурсия просто обходят вложенный список, поэтому они эквивалентны оптическому traverse . traverse. Итак, мы можем использовать:

setInitPrices' :: [[Typex]] -> [[Typex]]
setInitPrices' = traverse .> traverse .> connections .> traversed <. _3 .@~ f
  where f i = 2 * fromIntegral i

Наконец, возможно, стоит отметить, что если у вас сложная индексированная оптика, например:

a .> b .> c .> d <. e <. f <. g

по неясным причинам (правильная ассоциативность операторов и тот факт, что .> идентично .) это всегда эквивалентно:

a . b . c .> d <. e . f . g

и это более распространенный способ написать это. Итак, окончательный вариант setInitPrices' будет таким:

setInitPrices' :: [[Typex]] -> [[Typex]]
setInitPrices' = traverse . traverse . connections .> traversed <. _3 .@~ f
  where f i = 2 * fromIntegral i
person K. A. Buhr    schedule 09.03.2020
comment
Именно то, что мне было нужно. Спасибо и за подробное объяснение. Пример в этом вопросе работает безупречно, но его использование в моем собственном коде приводит к следующей ошибке: * Ambiguous type variable t0 'возникает из-за использования traverse' prevents the constraint (Traversable t0)' из-за решения. Соответствующие привязки включают setInitPrices' :: t0 (t1 MyType) -> t0 (t1 MyType) Возможное исправление: используйте аннотацию типа, чтобы указать, что t0' should be Тип функции четко указан, поэтому какую аннотацию это может означать? - person LHurttila; 10.03.2020
comment
Это сообщение утверждает, что setinitPrices' не имеет сигнатуры типа. Возможно, вы по-разному пишете имена функций в сигнатуре и определении типа? - person K. A. Buhr; 10.03.2020